前段时间研究nandflash驱动,很好奇,到底是怎么探测nandflash,以及驱动等等。好吧,现在开始不说了。
首先在板级文件里面必须添加设备信息,
static struct resource s5pv210_nand_resource[] = {
[0] = {
.start = 0xB0E00000,
.end = 0xB0E00000 + 0x00100000 - 1,
.flags = IORESOURCE_MEM,
}
};
struct platform_device s5pv210_device_nand = {
.name = "s5pv210-nand",
.id = -1,
.num_resources = ARRAY_SIZE(s5pv210_nand_resource),
.resource = s5pv210_nand_resource,
};
并将s5pv210_device_nand添加到struct platform_device *smdkv210_devices[] ,
在smdkv210_machine_init会调用platform_add_devices(smdkv210_devices, ARRAY_SIZE(smdkv210_devices));
该函数会将所有的设备注册。以后注册相应的driver时会查找对应设备的信息,此时设备早已注册。
先看s5pv210_nand.c文件,这个不是官方内核源码的文件,不过也差不多。
static int __init s3c_nand_init(void)
{
printk("S3C NAND Driver, (c) 2008 Samsung Electronics\n");
return platform_driver_register(&s5pc110_nand_driver);
}
在内核初始化后肯定有S3C NAND Driver, (c) 2008 Samsung Electronics 这个信息输出。
这个函数被放在init节,会被调用。进而调用关键的platform_driver_register(&s5pc110_nand_driver);
进入platform_driver_register会调用driver_register(&drv->driver)(相当重要的设备驱动架构的函数)
driver_register->bus_add_driver(struct device_driver *drv),这个就是在/sys/ bus里面添加该driver的信息。
总之一系列的操作,要和内核驱动架构对接。下面来到了重要的地方,
if (drv->bus->p->drivers_autoprobe) {
error = driver_attach(drv);
if (error)
goto out_unregister;
}
显然如果drivers_autoprobe为真的话就会自动探测设备。进入driver_attach,该函数很简单调用bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
bus_for_each_dev就是迭代寻找该bus下的设备,进行匹配。用这个函数__driver_attach进行判断。
static int __driver_attach(struct device *dev, void *data)
{
struct device_driver *drv = data;
/*
* Lock device and try to bind to it. We drop the error
* here and always return 0, because we need to keep trying
* to bind to devices and some drivers will return an error
* simply if it didn't support the device.
*
* driver_probe_device() will spit a warning if there
* is an error.
*/
if (!driver_match_device(drv, dev))
return 0;
if (dev->parent) /* Needed for USB */
device_lock(dev->parent);
device_lock(dev);
if (!dev->driver)
driver_probe_device(drv, dev);
device_unlock(dev);
if (dev->parent)
device_unlock(dev->parent);
return 0;
}
driver_match_device会调用drv->bus->match(dev, drv),即该总线的match函数,如果成功,并且dev没有绑定,
则会调用driver_probe_device(drv, dev) --》really_probe(dev, drv);
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; //将driver和dev真正绑定
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;
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);
}
/*
* 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;
}
最后我们来看看设备探测函数是哪个
static struct platform_driver s5pc110_nand_driver = {
.probe = s5pc110_nand_probe,
.remove = s3c_nand_remove,
.suspend = s3c_nand_suspend,
.resume = s3c_nand_resume,
.driver = {
.name = "s5pv210-nand",
.owner = THIS_MODULE,
},
};
即最终会调用s5pc110_nand_probe--->>s3c_nand_probe
在s3c_nand_probe里面,会读取s5pv210_device_nand(板级文件里面定义的)结构信息,该结构包括nand分区信息,访问io的地址等等。
根据这些信息,访问io,获得nand的设备信息
tmp = readb(nand->IO_ADDR_R); /* Maf. ID */
tmp = readb(nand->IO_ADDR_R); /* Device ID */
会进行匹配,成功则,继续进行nand的初始化。这个可以自己去看看该文件。