一、驅動中的probe函數如何被調用?
首先,我們知道驅動執行的起始函數是init函數。以I2C驅動爲例。
(cm36283.c)
- static int __init cm36283_init(void)
- {
- int err = 0;
- err = i2c_add_driver(&cm36283_driver);
- return err;
- }
-
- module_init(cm36283_init);
(i2c.h)
- #define i2c_add_driver(driver) \
- i2c_register_driver(THIS_MODULE, driver)
(i2c-core.c)
- int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
- {
- int res;
-
-
- if (unlikely(WARN_ON(!i2c_bus_type.p)))
- return -EAGAIN;
-
-
- driver->driver.owner = owner;
- driver->driver.bus = &i2c_bus_type;
-
-
-
-
- res = driver_register(&driver->driver);</span>
- if (res)
- return res;
-
-
- if (driver->suspend)
- pr_warn("i2c-core: driver [%s] using legacy suspend method\n",
- driver->driver.name);
- if (driver->resume)
- pr_warn("i2c-core: driver [%s] using legacy resume method\n",
- driver->driver.name);
-
- pr_debug("i2c-core: driver [%s] registered\n", driver->driver.name);
-
- INIT_LIST_HEAD(&driver->clients);
-
- i2c_for_each_dev(driver, __process_new_driver);
-
- return 0;
- }
- EXPORT_SYMBOL(i2c_register_driver);
(driver.c)
- int driver_register(struct device_driver *drv)
- {
- int ret;
- struct device_driver *other;
-
- BUG_ON(!drv->bus->p);
-
- if ((drv->bus->probe && drv->probe) ||
- (drv->bus->remove && drv->remove) ||
- (drv->bus->shutdown && drv->shutdown))
- printk(KERN_WARNING "Driver '%s' needs updating - please use "
- "bus_type methods\n", drv->name);
-
- other = driver_find(drv->name, drv->bus);
- if (other) {
- printk(KERN_ERR "Error: Driver '%s' is already registered, "
- "aborting...\n", drv->name);
- return -EBUSY;
- }
-
- ret = bus_add_driver(drv);</span>
- if (ret)
- return ret;
- ret = driver_add_groups(drv, drv->groups);
- if (ret)
- bus_remove_driver(drv);
- return ret;
- }
(bus.c)
- int bus_add_driver(struct device_driver *drv)
- {
- struct bus_type *bus;
- struct driver_private *priv;
- int error = 0;
-
- bus = bus_get(drv->bus);
- if (!bus)
- return -EINVAL;
-
- pr_debug("bus: '%s': add driver %s\n", bus->name, drv->name);
-
- priv = kzalloc(sizeof(*priv), GFP_KERNEL);
- if (!priv) {
- error = -ENOMEM;
- goto out_put_bus;
- }
- klist_init(&priv->klist_devices, NULL, NULL);
- priv->driver = drv;
- drv->p = priv;
- priv->kobj.kset = bus->p->drivers_kset;
- error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
- "%s", drv->name);
- if (error)
- goto out_unregister;
-
- klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
- if (drv->bus->p->drivers_autoprobe) {
- <span style="color:#FF6666;">error = driver_attach(drv);</span>
- if (error)
- goto out_unregister;
- }
- module_add_driver(drv->owner, drv);
-
- error = driver_create_file(drv, &driver_attr_uevent);
- if (error) {
- printk(KERN_ERR "%s: uevent attr (%s) failed\n",
- __func__, drv->name);
- }
- error = driver_add_attrs(bus, drv);
- if (error) {
-
- printk(KERN_ERR "%s: driver_add_attrs(%s) failed\n",
- __func__, drv->name);
- }
-
- if (!drv->suppress_bind_attrs) {
- error = add_bind_files(drv);
- if (error) {
-
- printk(KERN_ERR "%s: add_bind_files(%s) failed\n",
- __func__, drv->name);
- }
- }
-
- kobject_uevent(&priv->kobj, KOBJ_ADD);
- return 0;
-
- out_unregister:
- kobject_put(&priv->kobj);
- kfree(drv->p);
- drv->p = NULL;
- out_put_bus:
- bus_put(bus);
- return error;
- }
driver註冊到bus上時,同時會建立uevent屬性文件及bus->attr數組中的屬性文件(如果
bus->attr
有的話)。
(dd.c)
- int driver_attach(struct device_driver *drv)
- {
- return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
- }
- EXPORT_SYMBOL_GPL(driver_attach);
(bus.c)
- int bus_for_each_dev(struct bus_type *bus, struct device *start,
- void *data, int (*fn)(struct device *, void *))
- {
- struct klist_iter i;
- struct device *dev;
- int error = 0;
-
- if (!bus)
- return -EINVAL;
-
- klist_iter_init_node(&bus->p->klist_devices, &i,
- (start ? &start->p->knode_bus : NULL));
- while ((dev = next_device(&i)) && !error)
- error = <span style="color:#FF6666;">fn(dev, data);</span>
- klist_iter_exit(&i);
- return error;
- }
- EXPORT_SYMBOL_GPL(bus_for_each_dev);
(dd.c)
- static int __driver_attach(struct device *dev, void *data)
- {
- struct device_driver *drv = data;
-
-
-
-
-
-
-
-
-
-
-
- if (!driver_match_device(drv, dev))</strong>
- return 0;
-
- if (dev->parent)
- device_lock(dev->parent);
- device_lock(dev);
- if (!dev->driver)
- <strong><span style="color:#FF6666;">driver_probe_device(drv, dev);</span></strong>
- device_unlock(dev);
- if (dev->parent)
- device_unlock(dev->parent);
-
- return 0;
- }
driver_match_device调用bus上的match函数进行匹配。具体可以参照各种总线match匹配函数
匹配成功后,会调用driver的probe函数。
- 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_get_noresume(dev);
- pm_runtime_barrier(dev);
- <span style="color:#FF6666;">ret = really_probe(dev, drv);</span>
- pm_runtime_put_sync(dev);
-
- return ret;
- }
- static int really_probe(struct device *dev, struct device_driver *drv)
- {
- int ret = 0;
-
- 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;
-
-
- 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) {
- <span style="color:#FF6666;">ret = drv->probe(dev);</span>
- 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;
-
- if (ret == -EPROBE_DEFER) {
-
- dev_info(dev, "Driver %s requests probe deferral\n", drv->name);
- driver_deferred_probe_add(dev);
- } else if (ret != -ENODEV && ret != -ENXIO) {
-
- 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);
- }
-
-
-
-
- ret = 0;
- done:
- atomic_dec(&probe_count);
- wake_up(&probe_waitqueue);
- return ret;
- }
如果bus->probe爲空,就執行drv->probe
二、驅動中的probe函數參數如何產生的?
根據函數參數傳遞可以跟蹤到,是從bus_for_each_dev中獲得參數的。
- int driver_attach(struct device_driver *drv)
- {
- return bus_for_each_dev(<span style="color:#FF0000;"><strong>drv->bus, NULL, drv, __driver_attach</strong></span>);
- }
- EXPORT_SYMBOL_GPL(driver_attach);
-
- int bus_for_each_dev(struct bus_type *bus, struct device *start,
- void *data, int (*fn)(struct device *, void *))
- {
- struct klist_iter i;
- struct device *dev;
- int error = 0;
-
- if (!bus)
- return -EINVAL;
-
- klist_iter_init_node(&bus->p->klist_devices, &i,
- (start ? &start->p->knode_bus : NULL));
- while ((<strong><span style="color:#FF0000;">dev = next_device(&i)</span></strong>) && !error)
- error = fn(dev, data);
- klist_iter_exit(&i);
- return error;
- }
這樣可以看到klist_iter_init_node實際調用klist_iter_init_node(&bus->p->klist_devices, &i,NULL),所以,跟蹤klist_iter_init_node函數,發現並沒做什麼相關重要的東西。
- void klist_iter_init_node(struct klist *k, struct klist_iter *i,
- struct klist_node *n)
- {
- i->i_klist = k;
- i->i_cur = n;
- if (n)
- kref_get(&n->n_ref);
- }
- EXPORT_SYMBOL_GPL(klist_iter_init_node);
- static struct device *next_device(struct klist_iter *i)
- {
- struct klist_node *n = klist_next(i);
- struct device *dev = NULL;
- struct device_private *dev_prv;
-
- if (n) {
- dev_prv =<span style="color:#FF0000;"> to_device_private_bus(n);</span>
- <span style="color:#FF0000;">dev = dev_prv->device;</span>
- }
- return dev;
- }
可以看出,只要得到dev_prv就可以得到dev。下面的問題就是dev_prv中的dev如何被添加進去的呢?
- #define to_device_private_bus(obj) \
- container_of(obj, struct device_private, knode_bus)
這裏引用其他一些文章解釋:
先上几个struct:
struct klist_iter {
struct klist *i_klist;
struct klist_node *i_cur;
};
struct klist {
spinlock_t k_lock;
struct list_head k_list;
void (*get)(struct klist_node *);
void (*put)(struct klist_node *);
} __attribute__ ((aligned (sizeof(void*))));
struct klist_node {
void *n_klist; /* never access directly */
struct list_head n_node;
struct kref n_ref;
};
struct kref {
atomic_t refcount;
};
其中的klist_iter_init_node(&bus->p->klist_devices, &i,(start ?&start->p->knode_bus :NULL))作用是定义个klist_iter指向此klist,以便以后直接使用,如图:
- static struct device *next_device(struct klist_iter *i)
- {
- struct klist_node *n =<span style="color:#FF0000;"> <strong>klist_next(i);</strong></span>
- struct device *dev = NULL;
- struct device_private *dev_prv;
-
- if (n) {
- dev_prv = to_device_private_bus(n);
- dev = dev_prv->device;
- }
- return dev;
- }
(klist.c)
- struct klist_node *klist_next(struct klist_iter *i)
- {
- void (*put)(struct klist_node *) = i->i_klist->put;
- struct klist_node *last = i->i_cur; <span style="color:#FFCCCC;"> <span style="color:#009900;">
- struct klist_node *next;
-
- spin_lock(&i->i_klist->k_lock);
-
- if (last) {
- next = to_klist_node(last->n_node.next);
- if (!klist_dec_and_del(last))
- put = NULL;
- } else
- <span style="color:#FF0000;">next = to_klist_node(i->i_klist->k_list.next);</span>
-
- i->i_cur = NULL;
- while (next != to_klist_node(&i->i_klist->k_list)) {
- if (likely(!knode_dead(next))) {
- kref_get(&next->n_ref);
- i->i_cur = next;
- break;
- }
- next = to_klist_node(next->n_node.next);
- }
-
- spin_unlock(&i->i_klist->k_lock);
-
- if (put && last)
- put(last);
- return i->i_cur;
- }
- EXPORT_SYMBOL_GPL(klist_next);
這裏可以知道i->i_cur=NULL,i->i_klist=&bus->p->klist_devices。
to_klist_node
從klist中取得下一個node賦值給next,然後執行一次while循環裏的內容,將next賦值給i->i_cur。這樣就得到了klist_node. to_device_private_bus通過container_of 得到struct device_private。下圖說明其中的關係。
由此,可以知道DTS解析後的設備都是通過klist和klist_node掛載到bus上的。
三、 DTS的解析掛載
dts文件編譯時通過dtc被編譯成了dtb文件。然後在內核啓動時該文件被解析。DTS节点信息保存到allnodes鏈表中。
start_kernel() --> setup_arch() --> unflatten_device_tree()
随后,当系统启动到board文件时,会调用.init_machine,高通8974平台对应的是msm8974_init()。接着调用of_platform_populate(....)接口,加载平台总线和平台设备。
(kernel/arch/arm/mach-msm/board-8226.c)
- void __init msm8226_init(void)
- {
- board_dt_populate(adata);</span>
- msm8226_add_drivers();
- }
(kernel/arch/arm/mach-msm/board-dt.c)
- void __init board_dt_populate(struct of_dev_auxdata *adata)
- {
- of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
-
-
-
-
- of_platform_populate(of_find_node_by_path("/soc"),
- of_default_bus_match_table, adata, NULL);
- }
上面兩句話,一個是以“/”爲根進行遍歷;一個是遍歷allnodes鏈找到“/soc”,並以這一節點爲根節點進行遍歷。
(kernel/driver/of/platform.c)
- 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;
- }
- #endif /* CONFIG_OF_ADDRESS */
of_platform_populate遍歷根節點下的所有節點,分配建立platform device。for_each_child_of_node實現遍歷父節點下的所有兄弟節點及孩子節點。
- #define for_each_child_of_node(parent, child) \
- for (child = of_get_next_child(parent, NULL); child != NULL; \
- child = of_get_next_child(parent, child))
- struct device_node *of_get_next_child(const struct device_node *node,
- struct device_node *prev)
- {
- struct device_node *next;
-
- read_lock(&devtree_lock);
- next = prev ? prev->sibling : node->child;
- for (; next; next = next->sibling)
- if (of_node_get(next))
- break;
- of_node_put(prev);
- read_unlock(&devtree_lock);
- return next;
- }
- EXPORT_SYMBOL(of_get_next_child);
- (kernel/driver/of/platform.c)
-
- 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;
-
-
- if (strict && (!of_get_property(bus, "compatible", NULL))) {
- pr_debug("%s() - skipping %s, no compatible prop\n",
- __func__, bus->full_name);
- return 0;
- }
-
- auxdata = of_dev_lookup(lookup, bus);
- if (auxdata) {
- bus_id = auxdata->name;
- platform_data = auxdata->platform_data;
- }
-
- if (of_device_is_compatible(bus, "arm,primecell")) {
- of_amba_device_create(bus, bus_id, platform_data, parent);
- return 0;
- }
-
- dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent);
- if (!dev || !of_match_node(matches, bus))
- return 0;
-
- 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;
- }
- }
- return rc;
- }
- 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))
- return NULL;
-
- dev = of_device_alloc(np, bus_id, parent);
- if (!dev)
- return NULL;
-
- #if defined(CONFIG_MICROBLAZE)
- dev->archdata.dma_mask = 0xffffffffUL;
- #endif
- dev->dev.coherent_dma_mask = DMA_BIT_MASK(sizeof(dma_addr_t) * 8);
- dev->dev.bus = &platform_bus_type;
- dev->dev.platform_data = platform_data;
-
-
-
-
-
-
- if (of_device_add(dev) != 0) {
- platform_device_put(dev);
- return NULL;
- }
-
- return dev;
- }
of_device_alloc根據allnodes的device node分配platform device空間。然後設置platform device的bus總線。
- 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;
-
- dev = platform_device_alloc("", -1);
- if (!dev)
- return NULL;
-
-
- if (of_can_translate_address(np))
- while (of_address_to_resource(np, num_reg, &temp_res) == 0)
- num_reg++;
- num_irq = of_irq_count(np);
-
-
- 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);
- }
- WARN_ON(of_irq_to_resource_table(np, res, num_irq) != num_irq);
- }
-
- dev->dev.of_node = of_node_get(np);
- #if defined(CONFIG_MICROBLAZE)
- dev->dev.dma_mask = &dev->archdata.dma_mask;
- #endif
- dev->dev.parent = parent;
-
- if (bus_id)
- dev_set_name(&dev->dev, "%s", bus_id);
- else
- of_device_make_bus_id(&dev->dev);
-
- return dev;
- }
- EXPORT_SYMBOL(of_device_alloc);
分配platform device空間後,注意將device node賦值給dev->dev.of_node,這也就是爲什麼經常在probe函數中見到通過pdev->dev.of_node得到dts 設備節點,並從該節點中讀取相關信息。
I2C platform device驅動加載在i2c-qup.c文件中。下面是i2c-qup platform device驅動加載及i2c-client的驅動加載。
(i2c-qup.c)
- static int __devinit
- qup_i2c_probe(struct platform_device *pdev)
- {
- struct qup_i2c_dev *dev;
- struct resource *qup_mem, *gsbi_mem, *qup_io, *gsbi_io, *res;
- struct resource *in_irq, *out_irq, *err_irq;
- struct clk *clk, *pclk;
- int ret = 0;
- int i;
- int dt_gpios[I2C_GPIOS_DT_CNT];
- bool use_device_tree = pdev->dev.of_node;
- struct msm_i2c_platform_data *pdata;
-
- gsbi_mem = NULL;
- dev_dbg(&pdev->dev, "qup_i2c_probe\n");
-
- if (use_device_tree) {
- pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
- if (!pdata)
- return -ENOMEM;
-
- ret = msm_i2c_rsrcs_dt_to_pdata_map(pdev, pdata, dt_gpios);
- if (ret)
- goto get_res_failed;
- } else
- pdata = pdev->dev.platform_data;
-
- if (!pdata) {
- dev_err(&pdev->dev, "platform data not initialized\n");
- return -ENOSYS;
- }
- qup_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM,
- "qup_phys_addr");
- if (!qup_mem) {
- dev_err(&pdev->dev,
- "platform_get_resource_byname(qup_phys_addr) failed\n");
- ret = -ENODEV;
- goto get_res_failed;
- }
-
-
-
-
-
- in_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
- "qup_in_intr");
-
- out_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
- "qup_out_intr");
-
- err_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
- "qup_err_intr");
- if (!err_irq) {
- dev_err(&pdev->dev, "no error irq resource?\n");
- ret = -ENODEV;
- goto get_res_failed;
- }
-
- qup_io = request_mem_region(qup_mem->start, resource_size(qup_mem),
- pdev->name);
- if (!qup_io) {
- dev_err(&pdev->dev, "QUP region already claimed\n");
- ret = -EBUSY;
- goto get_res_failed;
- }
- if (!pdata->use_gsbi_shared_mode) {
- gsbi_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM,
- "gsbi_qup_i2c_addr");
- if (!gsbi_mem) {
- dev_dbg(&pdev->dev, "Assume BLSP\n");
-
-
-
-
- goto blsp_core_init;
- }
- gsbi_io = request_mem_region(gsbi_mem->start,
- resource_size(gsbi_mem),
- pdev->name);
- if (!gsbi_io) {
- dev_err(&pdev->dev, "GSBI region already claimed\n");
- ret = -EBUSY;
- goto err_res_failed;
- }
- }
-
- blsp_core_init:
- clk = clk_get(&pdev->dev, "core_clk");
- if (IS_ERR(clk)) {
- dev_err(&pdev->dev, "Could not get core_clk\n");
- ret = PTR_ERR(clk);
- goto err_clk_get_failed;
- }
-
- pclk = clk_get(&pdev->dev, "iface_clk");
- if (IS_ERR(pclk)) {
- dev_err(&pdev->dev, "Could not get iface_clk\n");
- ret = PTR_ERR(pclk);
- clk_put(clk);
- goto err_clk_get_failed;
- }
-
-
- if (pdata->clk_freq <= 0 ||
- pdata->clk_freq > 400000) {
- dev_err(&pdev->dev, "clock frequency not supported\n");
- ret = -EIO;
- goto err_config_failed;
- }
-
- dev = kzalloc(sizeof(struct qup_i2c_dev), GFP_KERNEL);
- if (!dev) {
- ret = -ENOMEM;
- goto err_alloc_dev_failed;
- }
-
- dev->dev = &pdev->dev;
- if (in_irq)
- dev->in_irq = in_irq->start;
- if (out_irq)
- dev->out_irq = out_irq->start;
- dev->err_irq = err_irq->start;
- if (in_irq && out_irq)
- dev->num_irqs = 3;
- else
- dev->num_irqs = 1;
- dev->clk = clk;
- dev->pclk = pclk;
- dev->base = ioremap(qup_mem->start, resource_size(qup_mem));
- if (!dev->base) {
- ret = -ENOMEM;
- goto err_ioremap_failed;
- }
-
-
- if (gsbi_mem) {
- dev->gsbi = ioremap(gsbi_mem->start, resource_size(gsbi_mem));
- if (!dev->gsbi) {
- ret = -ENOMEM;
- goto err_gsbi_failed;
- }
- }
-
- for (i = 0; i < ARRAY_SIZE(i2c_rsrcs); ++i) {
- if (use_device_tree && i < I2C_GPIOS_DT_CNT) {
- dev->i2c_gpios[i] = dt_gpios[i];
- } else {
- res = platform_get_resource_byname(pdev, IORESOURCE_IO,
- i2c_rsrcs[i]);
- dev->i2c_gpios[i] = res ? res->start : -1;
- }
- }
-
- platform_set_drvdata(pdev, dev);
-
- dev->one_bit_t = (USEC_PER_SEC/pdata->clk_freq) + 1;
- dev->pdata = pdata;
- dev->clk_ctl = 0;
- dev->pos = 0;
-
- ret = i2c_qup_clk_path_init(pdev, dev);
- if (ret) {
- dev_err(&pdev->dev,
- "Failed to init clock path-voting data structs. err:%d", ret);
-
- dev->pdata->master_id = 0;
- }
-
- if (dev->pdata->src_clk_rate <= 0) {
- dev_info(&pdev->dev,
- "No src_clk_rate specified in platfrom data\n");
- dev_info(&pdev->dev, "Using default clock rate %dHz\n",
- DEFAULT_CLK_RATE);
- dev->pdata->src_clk_rate = DEFAULT_CLK_RATE;
- }
-
- ret = clk_set_rate(dev->clk, dev->pdata->src_clk_rate);
- if (ret)
- dev_info(&pdev->dev, "clk_set_rate(core_clk, %dHz):%d\n",
- dev->pdata->src_clk_rate, ret);
-
- clk_prepare_enable(dev->clk);
- clk_prepare_enable(dev->pclk);
-
-
-
-
- writel_relaxed(1, dev->base + QUP_SW_RESET);
- if (qup_i2c_poll_state(dev, 0, true) != 0)
- goto err_reset_failed;
- clk_disable_unprepare(dev->clk);
- clk_disable_unprepare(dev->pclk);
-
-
-
-
-
-
-
-
- if (dev->num_irqs == 3) {
- ret = request_irq(dev->in_irq, qup_i2c_interrupt,
- IRQF_TRIGGER_RISING, "qup_in_intr", dev);
- if (ret) {
- dev_err(&pdev->dev, "request_in_irq failed\n");
- goto err_request_irq_failed;
- }
-
-
-
-
- ret = request_irq(dev->out_irq, qup_i2c_interrupt,
- IRQF_TRIGGER_RISING, "qup_out_intr", dev);
- if (ret) {
- dev_err(&pdev->dev, "request_out_irq failed\n");
- free_irq(dev->in_irq, dev);
- goto err_request_irq_failed;
- }
- ret = request_irq(dev->err_irq, qup_i2c_interrupt,
- IRQF_TRIGGER_RISING, "qup_err_intr", dev);
- if (ret) {
- dev_err(&pdev->dev, "request_err_irq failed\n");
- free_irq(dev->out_irq, dev);
- free_irq(dev->in_irq, dev);
- goto err_request_irq_failed;
- }
- } else {
- ret = request_irq(dev->err_irq, qup_i2c_interrupt,
- IRQF_TRIGGER_HIGH, "qup_err_intr", dev);
- if (ret) {
- dev_err(&pdev->dev, "request_err_irq failed\n");
- goto err_request_irq_failed;
- }
- }
- disable_irq(dev->err_irq);
- if (dev->num_irqs == 3) {
- disable_irq(dev->in_irq);
- disable_irq(dev->out_irq);
- }
- i2c_set_adapdata(&dev->adapter, dev);
- dev->adapter.algo = &qup_i2c_algo;
- strlcpy(dev->adapter.name,
- "QUP I2C adapter",
- sizeof(dev->adapter.name));
- dev->adapter.nr = pdev->id;
- dev->adapter.dev.parent = &pdev->dev;
- if (pdata->msm_i2c_config_gpio)
- pdata->msm_i2c_config_gpio(dev->adapter.nr, 1);
-
- mutex_init(&dev->mlock);
- dev->pwr_state = MSM_I2C_PM_SUSPENDED;
-
-
-
-
- if (dev->pdata->keep_ahb_clk_on)
- clk_prepare_enable(dev->pclk);
-
- ret = i2c_add_numbered_adapter(&dev->adapter);
- if (ret) {
- dev_err(&pdev->dev, "i2c_add_adapter failed\n");
- if (dev->num_irqs == 3) {
- free_irq(dev->out_irq, dev);
- free_irq(dev->in_irq, dev);
- }
- free_irq(dev->err_irq, dev);
- } else {
- if (dev->dev->of_node) {
- dev->adapter.dev.of_node = pdev->dev.of_node;
- of_i2c_register_devices(&dev->adapter);
- }
-
- pm_runtime_set_autosuspend_delay(&pdev->dev, MSEC_PER_SEC);
- pm_runtime_use_autosuspend(&pdev->dev);
- pm_runtime_enable(&pdev->dev);
- return 0;
- }
-
-
- err_request_irq_failed:
- if (dev->gsbi)
- iounmap(dev->gsbi);
- err_reset_failed:
- clk_disable_unprepare(dev->clk);
- clk_disable_unprepare(dev->pclk);
- i2c_qup_clk_path_teardown(dev);
- err_gsbi_failed:
- iounmap(dev->base);
- err_ioremap_failed:
- kfree(dev);
- err_alloc_dev_failed:
- err_config_failed:
- clk_put(clk);
- clk_put(pclk);
- err_clk_get_failed:
- if (gsbi_mem)
- release_mem_region(gsbi_mem->start, resource_size(gsbi_mem));
- err_res_failed:
- release_mem_region(qup_mem->start, resource_size(qup_mem));
- get_res_failed:
- if (pdev->dev.of_node)
- kfree(pdata);
- return ret;
- }
i2c、spi等都是platform bus,在msm8974_init()時调用of_platform_populate(....)接口註冊到系統中。這樣在i2c bus驅動中就可以調用of_i2c_register_devices,將i2c設備註冊到i2c bus上。
(of_i2c.c)
- void of_i2c_register_devices(struct i2c_adapter *adap)
- {
- void *result;
- struct device_node *node;
-
-
- if (!adap->dev.of_node)
- return;
-
- dev_dbg(&adap->dev, "of_i2c: walking child nodes\n");
-
- for_each_child_of_node(adap->dev.of_node, node) {
- struct i2c_board_info info = {};
- struct dev_archdata dev_ad = {};
- const __be32 *addr;
- int len;
-
- dev_dbg(&adap->dev, "of_i2c: register %s\n", node->full_name);
-
- if (of_modalias_node(node, info.type, sizeof(info.type)) < 0) {
- dev_err(&adap->dev, "of_i2c: modalias failure on %s\n",
- node->full_name);
- continue;
- }
-
- addr = of_get_property(node, "reg", &len);
- if (!addr || (len < sizeof(int))) {
- dev_err(&adap->dev, "of_i2c: invalid reg on %s\n",
- node->full_name);
- continue;
- }
-
- info.addr = be32_to_cpup(addr);
- if (info.addr > (1 << 10) - 1) {
- dev_err(&adap->dev, "of_i2c: invalid addr=%x on %s\n",
- info.addr, node->full_name);
- continue;
- }
-
- info.irq = irq_of_parse_and_map(node, 0);
- info.of_node = of_node_get(node);
- info.archdata = &dev_ad;
-
- request_module("%s%s", I2C_MODULE_PREFIX, info.type);
-
- <span style="color:#FF0000;">result = i2c_new_device(adap, &info);</span>
- if (result == NULL) {
- dev_err(&adap->dev, "of_i2c: Failure registering %s\n",
- node->full_name);
- of_node_put(node);
- irq_dispose_mapping(info.irq);
- continue;
- }
- }
- }
- EXPORT_SYMBOL(of_i2c_register_devices);
of_i2c_register_devices從解析後的allnodes鏈表信息取出節點,解析具體信息後,放到stuct i2c_board_info中,用於建立i2c-client。
(I2c-core.c)
- struct i2c_client *
- i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
- {
- struct i2c_client *client;
- int status;
-
- client = kzalloc(sizeof *client, GFP_KERNEL);
- if (!client)
- return NULL;
-
- client->adapter = adap;
-
- client->dev.platform_data = info->platform_data;
-
- if (info->archdata)
- client->dev.archdata = *info->archdata;
-
- client->flags = info->flags;
- client->addr = info->addr;
- client->irq = info->irq;
-
- strlcpy(client->name, info->type, sizeof(client->name));
-
-
- status = i2c_check_client_addr_validity(client);
- if (status) {
- dev_err(&adap->dev, "Invalid %d-bit I2C address 0x%02hx\n",
- client->flags & I2C_CLIENT_TEN ? 10 : 7, client->addr);
- goto out_err_silent;
- }
-
-
- status = i2c_check_addr_busy(adap, client->addr);
- if (status)
- goto out_err;
-
- client->dev.parent = &client->adapter->dev;
- client->dev.bus = &i2c_bus_type;
- client->dev.type = &i2c_client_type;
- client->dev.of_node = info->of_node;
-
-
- dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap),
- client->addr | ((client->flags & I2C_CLIENT_TEN)
- ? 0xa000 : 0));
- status = device_register(&client->dev);
- if (status)
- goto out_err;
-
- dev_dbg(&adap->dev, "client [%s] registered with bus id %s\n",
- client->name, dev_name(&client->dev));
-
- return client;
-
- out_err:
- dev_err(&adap->dev, "Failed to register i2c client %s at 0x%02x "
- "(%d)\n", client->name, client->addr, status);
- out_err_silent:
- kfree(client);
- return NULL;
- }
- EXPORT_SYMBOL_GPL(i2c_new_device);
(bus.c)
- int device_register(struct device *dev)
- {
- device_initialize(dev);
- return device_add(dev);
- }
- 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;
- }
-
-
-
-
-
-
- if (dev->init_name) {
- dev_set_name(dev, "%s", dev->init_name);
- dev->init_name = NULL;
- }
-
-
- 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;
-
-
- if (parent)
- set_dev_node(dev, dev_to_node(parent));
-
-
-
- error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);
- if (error)
- goto Error;
-
-
- if (platform_notify)
- platform_notify(dev);
-
- error = device_create_file(dev, &uevent_attr);
- if (error)
- goto attrError;
-
- if (MAJOR(dev->devt)) {
- error = device_create_file(dev, &devt_attr);
- 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);
-
-
-
-
- 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);
-
- klist_add_tail(&dev->knode_class,
- &dev->class->p->klist_devices);
-
-
- 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, &devt_attr);
- ueventattrError:
- device_remove_file(dev, &uevent_attr);
- 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;
- }
-
- <pre name="code" class="cpp">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 = sysfs_create_link(&bus->p->devices_kset->kobj,
- &dev->kobj, dev_name(dev));
- if (error)
- goto out_id;
- 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_id:
- device_remove_attrs(bus, dev);
- out_put:
- bus_put(dev->bus);
- return error;
- }
這樣,i2c-client device掛載到了i2c bus上。當i2c 設備驅動添加到系統時,就出現了在前幾部分的東西了。從bus上取下i2c-client與i2c-driver進行匹配。
雖然i2-client怎麼掛載到i2c-bus上知道了(由qup_i2c_probe調用of_i2c_register_device掛載)。新的問題又來了:qup_i2c_probe參數是platform_device,i2c-bus又是如何建立platform_device的。
(kernel/include/linux/init.h)
- #define arch_initcall(fn) <span style="color:#FF0000;">__define_initcall("3",fn,3)</span>
- #define __define_initcall(level,fn,id) \
- static initcall_t __initcall_##fn##id __used \
- __attribute__((__section__(".initcall" level ".init"))) = fn
將i2c-bus的driver init函數放到.initcall3.init 代碼段中。
之所以,i2c-bus的driver和i2c 設備的驅動init函數使用的不同的函數放到.initcall代碼段,主要原因是後面調用.initcall中的函數執行順序是按.initcall段的函數順序進行的(按照initcall的level從0到7依次存放的)。
(kernel/include/linux/vmlinux.lds.h)
- #define INIT_CALLS_LEVEL(level) \
- VMLINUX_SYMBOL(__initcall##level##_start) = .; \
- *(.initcall##level##.init) \
- *(.initcall##level##s.init)
-
- #define INIT_CALLS \
- VMLINUX_SYMBOL(__initcall_start) = .; \
- *(.initcallearly.init) \
- INIT_CALLS_LEVEL(0) \
- INIT_CALLS_LEVEL(1) \
- INIT_CALLS_LEVEL(2) \
- INIT_CALLS_LEVEL(3) \
- INIT_CALLS_LEVEL(4) \
- INIT_CALLS_LEVEL(5) \
- INIT_CALLS_LEVEL(rootfs) \
- INIT_CALLS_LEVEL(6) \
- INIT_CALLS_LEVEL(7) \
- VMLINUX_SYMBOL(__initcall_end) = .;
那麼現在根據前面部分內容,知道driver_register將platform bus上的platform device取下來,與driver進行匹配。到此終於理順了從dts節點信息到驅動註冊的完整過程。
四、module_init()中的驅動初始化後函數何時被調用
(kernel/include/linux/init.h)
#define module_init(x) __initcall(x);
#define __initcall(fn) device_initcall(fn)
#define device_initcall(fn) __define_initcall("6",fn,6)
#define __define_initcall(level,fn,id) /
static initcall_t __initcall_##fn##id __used /
__attribute__((__section__(".initcall" level ".init"))) = fn
initcall_t(typedef int (*initcall_t)(void))
這樣就將module_init傳遞的fn函數放到了制定的.initcall6.init 代碼段中。
在linux /init/main.c 中,
static void __init do_initcalls(void)
{
initcall_t *call;
call = &__initcall_start;
do {
(*call)();
call++;
} while (call < &__initcall_end);
/* Make sure there is no pending stuff from the initcall sequence */
flush_scheduled_tasks();
}
通過do_initcalls就可以吧__initcall section段的函數調用起來。
參考文章: http://blog.csdn.net/MarsWG/archive/2004/11/05/168552.aspx