注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

还东国的博客

行之苟有恒,久久自芬芳

 
 
 

日志

 
 

Linux MTD子系统学习6—MTD设备的注册及流程之三  

2012-01-14 19:08:16|  分类: LINUX内核驱动 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
 

Linux MTD子系统学习6—MTD设备的注册及流程之三

今天说说MTD设备的注册过程,先从最初的宏开始:

MACHINE_START(S3C2440, "SMDK2440")

    /* Maintainer: Ben Dooks <ben@fluff.org> */

    .phys_io    = S3C2410_PA_UART,

    .io_pg_offst    = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,

    .boot_params    = S3C2410_SDRAM_PA + 0x100,

 

    .init_irq   = s3c24xx_init_irq,

    .map_io     = smdk2440_map_io,//必须先初对IO进行映射,注意。

    .init_machine   = smdk2440_machine_init,

    .timer      = &s3c24xx_timer,

MACHINE_END

其实在这个里边儿,用得到的主要是这个函数:

static void __init smdk2440_machine_init(void)

{

    s3c24xx_fb_set_platdata(&smdk2440_fb_info);

    s3c_i2c0_set_platdata(NULL);

 

    platform_add_devices(smdk2440_devices, ARRAY_SIZE(smdk2440_devices));

    smdk_machine_init();

}

 

在common-smdk2440.c中,即本文件中:

static struct s3c2410_platform_nand smdk_nand_info = {

    .tacls     = 20,

    .twrph0       = 60,

    .twrph1       = 20,

    .nr_sets   = ARRAY_SIZE(smdk_nand_sets),

    .sets      = smdk_nand_sets,

};

看最后的.sets:

static struct s3c2410_nand_set smdk_nand_sets[] = {

    [0] = {

       .name      = "NAND",

       .nr_chips  = 1,

       .nr_partitions    = ARRAY_SIZE(smdk_default_nand_part),

       .partitions   = smdk_default_nand_part,

    },

};

smdk_default_nand_part是什么东东:

static struct mtd_partition smdk_default_nand_part[] = {

    [0] = {

       .name  = "Boot Agent",

       .size  = SZ_16K,

       .offset    = 0,

    },

    [1] = {

       .name  = "S3C2410 flash partition 1",

       .offset = 0,

       .size  = SZ_2M,

    },

    [2] = {

       .name  = "S3C2410 flash partition 2",

       .offset = SZ_4M,

       .size  = SZ_4M,

    },

    [3] = {

       .name  = "S3C2410 flash partition 3",

       .offset    = SZ_8M,

       .size  = SZ_2M,

    },

    [4] = {

       .name  = "S3C2410 flash partition 4",

       .offset = SZ_1M * 10,

       .size  = SZ_4M,

    },

    [5] = {

       .name  = "S3C2410 flash partition 5",

       .offset    = SZ_1M * 14,

       .size  = SZ_1M * 10,

    },

    [6] = {

       .name  = "S3C2410 flash partition 6",

       .offset    = SZ_1M * 24,

       .size  = SZ_1M * 24,

    },

    [7] = {

       .name  = "S3C2410 flash partition 7",

       .offset = SZ_1M * 48,

       .size  = SZ_16M,

    }

};

哈哈,大家明白了吧,所有我们原来所考虑的东西,在这里都清楚明白的出现了,下面其实就是把这些注册到设备列表中去,在移植驱动的时候儿这些个是常见的,下面的真正的注册过程其实倒不是怎么总是看。

 

讲完了smdk_nand_info,看看另外一个重要的数据结构:

static struct platform_device __initdata *smdk_devs[] = {

    &s3c_device_nand,

    &smdk_led4,

    &smdk_led5,

    &smdk_led6,

    &smdk_led7,

};

如果定义中带有__initdata,那么这个代码会被放入.init.data的section中

struct platform_device s3c_device_nand = {

    .name        = "s3c2410-nand",//注意名字,这里非常滴重要

    .id      = -1,

    .num_resources      = ARRAY_SIZE(s3c_nand_resource),

    .resource    = s3c_nand_resource,

};

 

static struct resource s3c_nand_resource[] = {

    [0] = {

       .start = S3C_PA_NAND,// 0x4E000000  不同的宏有不同的定义地址,大家不要搞错

       .end   = S3C_PA_NAND + SZ_1M,// 0x4E000000+1M

       .flags = IORESOURCE_MEM,// 属于外设或者用于和设备通讯的支持直接寻址的地址空间.

    }

};

注意:

IORESOURCE_MEM 指的是属于外设或者用于和设备通讯的支持直接寻址的地址空间.PC机上,主板上北桥上连的内存都是交给kernel直接管理,或者都是用于软件执行,所以这部分内存不属于IORESOURCE_MEM, IORESOURCE_MEM主要是指PCI设备的 memory address space. 但在嵌入式上, 主板上的sdram一般是设备与cpu共享的. 故交给kernel直接管理的内存只是一部分.余下的内存以及寄存器空间都作为IORESOURCE_MEM来管理.

这个设备的数组里定义了smdk_devs这个设备数组,然后:

void __init smdk_machine_init(void)

{

    /* Configure the LEDs (even if we have no LED support)*/

 

    s3c2410_gpio_cfgpin(S3C2410_GPF(4), S3C2410_GPIO_OUTPUT);

    s3c2410_gpio_cfgpin(S3C2410_GPF(5), S3C2410_GPIO_OUTPUT);

    s3c2410_gpio_cfgpin(S3C2410_GPF(6), S3C2410_GPIO_OUTPUT);

    s3c2410_gpio_cfgpin(S3C2410_GPF(7), S3C2410_GPIO_OUTPUT);

 

    s3c2410_gpio_setpin(S3C2410_GPF(4), 1);

    s3c2410_gpio_setpin(S3C2410_GPF(5), 1);

    s3c2410_gpio_setpin(S3C2410_GPF(6), 1);

    s3c2410_gpio_setpin(S3C2410_GPF(7), 1);

 

    if (machine_is_smdk2443())

       smdk_nand_info.twrph0 = 50;

 

    s3c_device_nand.dev.platform_data = &smdk_nand_info;

 

    platform_add_devices(smdk_devs, ARRAY_SIZE(smdk_devs));

 

    s3c_pm_init();

}

在设备添加的函数里

int platform_add_devices(struct platform_device **devs, int num)

{

    int i, ret = 0;

 

    for (i = 0; i < num; i++) {

       ret = platform_device_register(devs[i]);

       if (ret) {

           while (--i >= 0)

              platform_device_unregister(devs[i]);

           break;

       }

    }

 

    return ret;

}

会调用

int platform_device_register(struct platform_device *pdev)

{

    device_initialize(&pdev->dev);

    return platform_device_add(pdev);

}

看到添加设备没有:

int platform_device_add(struct platform_device *pdev)

{

    int i, ret = 0;

 

    if (!pdev)

       return -EINVAL;

 

    if (!pdev->dev.parent)

       pdev->dev.parent = &platform_bus;

 

    pdev->dev.bus = &platform_bus_type;

 

    if (pdev->id != -1)

       dev_set_name(&pdev->dev, "%s.%d", pdev->name,  pdev->id);

    else

       dev_set_name(&pdev->dev, "%s", pdev->name);

 

    for (i = 0; i < pdev->num_resources; i++) {

       struct resource *p, *r = &pdev->resource[i];

 

       if (r->name == NULL)

           r->name = dev_name(&pdev->dev);

 

       p = r->parent;

       if (!p) {

           if (resource_type(r) == IORESOURCE_MEM)

              p = &iomem_resource;

           else if (resource_type(r) == IORESOURCE_IO)

              p = &ioport_resource;

       }

 

       if (p && insert_resource(p, r)) {

           printk(KERN_ERR

                  "%s: failed to claim resource %d\n",

                  dev_name(&pdev->dev), i);

           ret = -EBUSY;

           goto failed;

       }

    }

 

    pr_debug("Registering platform device '%s'. Parent at %s\n",

        dev_name(&pdev->dev), dev_name(pdev->dev.parent));

 

    ret = device_add(&pdev->dev);

    if (ret == 0)

       return ret;

 

 failed:

    while (--i >= 0) {

       struct resource *r = &pdev->resource[i];

       unsigned long type = resource_type(r);

 

       if (type == IORESOURCE_MEM || type == IORESOURCE_IO)

           release_resource(r);

    }

 

    return ret;

}

在这里把设备注册进平台BUS中去,其实设备可以不必注册,但驱动必须得注册,哪儿都不讲理,大家要有心理准备,反正按名索骥即可。

然后:

art 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.

 *

 * 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 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;

    }

 

    if (!dev_name(dev))

       goto name_error;

 

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

 

    parent = get_device(dev->parent);

    setup_parent(dev, parent);

 

    /* 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, &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);

 

    /* Notify clients of device addition.  This call must come

     * after dpm_sysf_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->class_mutex);

       /* tie the class to the device */

       klist_add_tail(&dev->knode_class,

                  &dev->class->p->class_devices);

 

       /* notify any interfaces that the device is here */

       list_for_each_entry(class_intf,

                  &dev->class->p->class_interfaces, node)

           if (class_intf->add_dev)

              class_intf->add_dev(dev, class_intf);

       mutex_unlock(&dev->class->p->class_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))

       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;

}

这其实就是LINUX内核上那一大套,在链表里操作,再添加一些链接。创建节点啥滴。

 

现在我们回头看:

struct device {  

    struct device       *parent;  

  

    struct device_private   *p;  

  

    struct kobject kobj;  

    const char      *init_name; /* initial name of the device */  

    struct device_type  *type;  

  

    struct semaphore    sem;    /* semaphore to synchronize calls to 

                     * its driver. 

                     */  

  

    struct bus_type *bus;       /* type of bus device is on */  

    struct device_driver *driver;   /* which driver has allocated this 

                       device */  

    void        *platform_data; /* Platform specific data, device 

                       core doesn't touch it */  

    struct dev_pm_info  power;  

  

#ifdef CONFIG_NUMA   

    int     numa_node;  /* NUMA node this device is close to */  

#endif   

    u64     *dma_mask;  /* dma mask (if dma'able device) */  

    u64     coherent_dma_mask;/* Like dma_mask, but for 

                         alloc_coherent mappings as 

                         not all hardware supports 

                         64 bit addresses for consistent 

                         allocations such descriptors. */  

  

    struct device_dma_parameters *dma_parms;  

  

    struct list_head    dma_pools;  /* dma pools (if dma'ble) */  

  

    struct dma_coherent_mem *dma_mem; /* internal for coherent mem 

                         override */  

    /* arch specific additions */  

    struct dev_archdata archdata;   

  

    dev_t           devt;   /* dev_t, creates the sysfs "dev" */  

  

    spinlock_t      devres_lock;  

    struct list_head    devres_head;  

  

    struct klist_node   knode_class;  

    struct class        *class;  

    const struct attribute_group **groups;  /* optional groups */  

  

    void    (*release)(struct device *dev);  

}; 

先来分析下struct device的结构变量。首先是指向父节点的指针parent,kobj是内嵌在device中的kobject,用于把它联系到sysfs中。bus是对设备所在总线的指针,driver是对设备所用驱动的指针。还有DMA需要的数据,表示设备号的devt,表示设备资源的devres_head和保护它的devres_lock。指向类的指针class,knode_class是被连入class链表时所用的klist节点。group是设备的属性集合。release应该是设备释放时调用的函数。

 

struct device_private {  

    struct klist klist_children;  

    struct klist_node knode_parent;  

    struct klist_node knode_driver;  

    struct klist_node knode_bus;  

    void *driver_data;  

    struct device *device;  

};  

#define to_device_private_parent(obj)   \   

    container_of(obj, struct device_private, knode_parent)  

#define to_device_private_driver(obj)   \    

    container_of(obj, struct device_private, knode_driver)  

#define to_device_private_bus(obj)  \   

    container_of(obj, struct device_private, knode_bus) 

struct device中有一部分不愿意让外界看到,所以做出struct device_private结构,包括了设备驱动模型内部的链接。klist_children是子设备的链表,knode_parent是连入父设备的klist_children时所用的节点,knode_driver是连入驱动的设备链表所用的节点,knode_bus是连入总线的设备链表时所用的节点。driver_data用于在设备结构中存放相关的驱动信息,也许是驱动专门为设备建立的结构实例。device则是指向struct device_private所属的device。

 

下面还有一些宏,to_device_private_parent()是从父设备的klist_children上节点,获得相应的device_private。to_device_private_driver()是从驱动的设备链表上节点,获得对应的device_private。to_device_private_bus()是从总线的设备链表上节点,获得对应的device_private。

 

或许会奇怪,为什么knode_class没有被移入struct device_private,或许有外部模块需要用到它。

这回硬件的注册清楚了吧,这里只讲基本的流程,对于细节大家可以去网上查找,这里不能把大段大段的书的代码和文字摘录过来,没有什么实际意义,只要能从整体上把握这个流程,用到的时候儿认真再翻也为时不晚,或者平时有时间就看看,来回从两头慢慢向中间对,挤干净内核。让他在我们的面前清楚明白,纤毫毕现。

最后的一句话:

只要努力,就会成功;只要学习,就一定能搞清楚,搞明白。

  评论这张
 
阅读(964)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017