平台设备的识别问题
作者:曹忠明, 华清远见嵌入式学院 讲师。
在初学系统移植的时候,很多同学碰到这样的问题,比如要添加LCD的支持,网上很多资料说要添加一些代码,可是为什么添加这些代码缺不是很清楚。这里我们分析一些这些代码和驱动之间的关系。
比如我们这里要添加LCD的支持,以S3C2410为例,我们会在arch/arm/mach-s3c2410/mach-smdk2410.c中添加如下代码:
staticstruct s3c2410fb_display s3c2410_lcd_cfg[] __initdata = {
        {
                .lcdcon5 = S3C2410_LCDCON5_FRM565 |
                        S3C2410_LCDCON5_INVVCLK |
                        S3C2410_LCDCON5_INVVLINE |
                        S3C2410_LCDCON5_INVVFRAME |
                        S3C2410_LCDCON5_PWREN |
                        S3C2410_LCDCON5_HWSWP,
                .type = S3C2410_LCDCON1_TFT,
                        .width = 320,
                        .height = 240,
                        .pixclock = 100000, /* HCLK/10 */
                        .xres = 320,
                        .yres = 240,
                        .bpp = 16,
                        .left_margin = 13,
                        .right_margin = 8,
                        .hsync_len = 4,
                        .upper_margin = 2,
                        .lower_margin = 7,
                        .vsync_len = 4,
                        },
        };
        staticstruct s3c2410fb_mach_info s3c2410_fb_info __initdata = {
                .displays = s3c2410_lcd_cfg,
                .num_displays = ARRAY_SIZE(s3c2410_lcd_cfg),
                .default_display = 0,
                .lpcsel = ((0xCE6) & ~7) | 1<<4,
        };
在函数smdk2410_init中添加如下内容
s3c24xx_fb_set_platdata(&s3c2410_fb_info);
添加这些代码之间的关系是什么我呢,我们发现前两个结构体之间有包含关系,这些数据用来描述我们LCD屏的物理特性。而s3c24xx_fb_set_platdata(&s3c2410_fb_info)才是使用这些变量的函数。这个函数做了些什么工作呢,我们看下他的源代码:
arch/arm/plat-s3c24xx/devs.c
        void __init s3c24xx_fb_set_platdata(struct s3c2410fb_mach_info *pd)
        {
                struct s3c2410fb_mach_info *npd;
                npd = kmemdup(pd, sizeof(*npd), GFP_KERNEL);
                if (npd) {
                         s3c_device_lcd.dev.platform_data = npd;
                        npd->displays = kmemdup(pd->displays,
                                sizeof(struct s3c2410fb_display) * npd->num_displays,
                                GFP_KERNEL);
                        if (!npd->displays)
                                printk(KERN_ERR "no memory for LCD display data\n");
                } else {
                        printk(KERN_ERR "no memory for LCD platform data\n");
                }
        }
s3c_device_lcd.dev.platform_data = npd就是将我们用来描述LCD的物理信息的结构体赋值给s3c_device_lcd的成员,s3c_device_lcd又是什么定义呢,在那里使用的呢?
staticstruct resource s3c_lcd_resource[] = {
                [0] = {
                        .start = S3C24XX_PA_LCD,
                        .end = S3C24XX_PA_LCD + S3C24XX_SZ_LCD - 1,
                        .flags = IORESOURCE_MEM,
                        },
                [1] = {
                        .start = IRQ_LCD,
                        .end = IRQ_LCD,
                        .flags = IORESOURCE_IRQ,
                        }
        };
        static u64 s3c_device_lcd_dmamask = 0xffffffffUL;
        structplatform_device s3c_device_lcd = {
                .name = "s3c2410-lcd",
                .id = -1,
                .num_resources = ARRAY_SIZE(s3c_lcd_resource),
                .resource = s3c_lcd_resource,
                .dev = {
                        .dma_mask = &s3c_device_lcd_dmamask,
                        .coherent_dma_mask = 0xffffffffUL
                }
        };
这里定义了s3c_device_lcd也就是我们的平台设备,这个结构用来描述lcd的物理信息,比如控制器的物理地址,及屏的物理信息。那么这个内容如何去操作我们的LCD呢。
这里涉及到linux内核的设计思想,linux内核想把设备与驱动完全分离,设备是设备,驱动是驱动。所以就出现了平台设备(platform_deivce)和驱动(platform_driver),这两个结果互不相干,一个用来描述设备,一个是驱动,但是他们有共同的成员是bus_type通过是个东西进行关联。上面我们定义了一个平台设备,并且在arch/arm/mach-s3c2410/mach-smdk2410.c中将这个设备注册到我们的系统中去。注册流程为:
在s3c2410fb.c中驱动中定义了这么一个结构:
staticstructplatform_driver s3c2410fb_driver = {
                .probe = s3c2410fb_probe,
                .remove = __devexit_p(s3c2410fb_remove),
                .suspend = s3c2410fb_suspend,
                .resume = s3c2410fb_resume,
                .driver = {
                        .name = "s3c2410-lcd",
                        .owner = THIS_MODULE,
                },
        };
我们看一下这个驱动的注册流程吧:
这里在最后,我们的驱动通过“name”找到了它的平台设备,并在执行probe的时候把,平台设备的相关内容带进来。其实设备和驱动的识别是相互的,先添加驱动后添加设备则是设备找驱动,先添加设备后添加驱动则是驱动找设备,不过现在的内核里多数情况是后者,也就是先添加了设备后添加驱动,在驱动注册的时候查询设备列表,并用相关的数据对我们的设备进行初始化!