Linux驱动学习(二)
2012-05-08 16:26
232 查看
注:基于Linux-2.6.38
上一篇里介绍了Linux驱动里比较底层的内容,这里将以具体的平台(mini6410)来介绍平台设备(platform device,如IIC,UART,RTC)在初始化时是怎么注册进内核的。
还是/arch/arm/mach-s3c64xx/mach-mini6410.c这个文件,前面有篇文章已经说了里面的mini6410_machine_init()函数是什么时候被调用的,因此在这里不再重复,直接看这个函数里面的内容:
虽说有点长,但不难理解,都是对平台设备相关数据的初始化还有一些寄存器的设置,重点看第66行的platform_add_devices()函数,该函数实现了平台设备的注册,第一个参数是结构体数组,第二个参数是该数组的大小,去看看它的定义:
嗯,该数组里存放了很多设备,这些设备都会随着mini6410_machine_init()函数被调用而被初始化。接下来回到platform_add_devices(),它在drivers/base/platform.c里定义:
在一个for循环里通过platform_device_register()将所有定义了的平台设备逐个注册进系统。看第6行platform_device_register()的定义:
第3行的函数在上一篇已经说过,看第4行的platform_device_add():
第9行,把上一篇说过的platform_bus作为当前设备的父设备;第11行,设置当前设备所在的总线;第13~16行,pdev->id这个成员在定义平台设备时已经赋值,大部分时候被置为-1;第18~39行是对当前平台设备占有的资源(如:寄存器,中断等)报告给系统,也就是说当你要使用这个设备时要先向系统申请关于这个设备的资源,然后才可以对该设备进行操作;第44行,很熟悉了,在上一篇已经说过。
上一篇里介绍了Linux驱动里比较底层的内容,这里将以具体的平台(mini6410)来介绍平台设备(platform device,如IIC,UART,RTC)在初始化时是怎么注册进内核的。
还是/arch/arm/mach-s3c64xx/mach-mini6410.c这个文件,前面有篇文章已经说了里面的mini6410_machine_init()函数是什么时候被调用的,因此在这里不再重复,直接看这个函数里面的内容:
static void __init mini6410_machine_init(void) { u32 cs1; s3c_i2c0_set_platdata(NULL); #ifdef CONFIG_S3C_DEV_I2C1 s3c_i2c1_set_platdata(NULL); #endif s3c_fb_set_platdata(&mini6410_lcd_pdata); #ifdef CONFIG_SAMSUNG_DEV_TS s3c24xx_ts_set_platdata(&s3c_ts_platform); #endif #ifdef CONFIG_TOUCHSCREEN_MINI6410 s3c_ts_set_platdata(&s3c_ts_platform); #endif s3c_sdhci0_set_platdata(&mini6410_hsmmc0_pdata); s3c_sdhci1_set_platdata(&mini6410_hsmmc1_pdata); #ifdef CONFIG_MTD_NAND_S3C s3c_device_nand.name = "s3c6410-nand"; #endif s3c_nand_set_platdata(&mini6410_nand_info); s3c64xx_ac97_setup_gpio(0); /* configure nCS1 width to 16 bits */ cs1 = __raw_readl(S3C64XX_SROM_BW) & ~(S3C64XX_SROM_BW__CS_MASK << S3C64XX_SROM_BW__NCS1__SHIFT); cs1 |= ((1 << S3C64XX_SROM_BW__DATAWIDTH__SHIFT) | (1 << S3C64XX_SROM_BW__WAITENABLE__SHIFT) | (1 << S3C64XX_SROM_BW__BYTEENABLE__SHIFT)) << S3C64XX_SROM_BW__NCS1__SHIFT; __raw_writel(cs1, S3C64XX_SROM_BW); /* set timing for nCS1 suitable for ethernet chip */ __raw_writel((0 << S3C64XX_SROM_BCX__PMC__SHIFT) | (6 << S3C64XX_SROM_BCX__TACP__SHIFT) | (4 << S3C64XX_SROM_BCX__TCAH__SHIFT) | (1 << S3C64XX_SROM_BCX__TCOH__SHIFT) | (0xe << S3C64XX_SROM_BCX__TACC__SHIFT) | (4 << S3C64XX_SROM_BCX__TCOS__SHIFT) | (0 << S3C64XX_SROM_BCX__TACS__SHIFT), S3C64XX_SROM_BC1); gpio_request(S3C64XX_GPN(5), "LCD power"); gpio_request(S3C64XX_GPF(13), "LCD power"); gpio_request(S3C64XX_GPF(15), "LCD power"); if (ARRAY_SIZE(i2c_devs0)) { i2c_register_board_info(0, i2c_devs0, ARRAY_SIZE(i2c_devs0)); } if (ARRAY_SIZE(i2c_devs1)) { i2c_register_board_info(1, i2c_devs1, ARRAY_SIZE(i2c_devs1)); } #ifdef CONFIG_S3C64XX_DEV_FIMC0 s3c_fimc0_set_platdata(NULL); #endif #ifdef CONFIG_S3C64XX_DEV_FIMC1 s3c_fimc1_set_platdata(NULL); #endif platform_add_devices(mini6410_devices, ARRAY_SIZE(mini6410_devices)); #ifdef CONFIG_VIDEO_SAMSUNG create_proc_read_entry("videomem", 0, NULL, s3c_media_read_proc, NULL); #endif }
虽说有点长,但不难理解,都是对平台设备相关数据的初始化还有一些寄存器的设置,重点看第66行的platform_add_devices()函数,该函数实现了平台设备的注册,第一个参数是结构体数组,第二个参数是该数组的大小,去看看它的定义:
static struct platform_device *mini6410_devices[] __initdata = { #ifdef CONFIG_MINI6410_SD_CH0 &s3c_device_hsmmc0, #endif #ifdef CONFIG_MINI6410_SD_CH1 &s3c_device_hsmmc1, #endif &s3c_device_i2c0, #ifdef CONFIG_S3C_DEV_I2C1 &s3c_device_i2c1, #endif &s3c_device_nand, //LCD设备 &s3c_device_fb, &s3c_device_ohci, &s3c_device_usb_hsotg, #ifdef CONFIG_SND_SAMSUNG_AC97 &s3c64xx_device_ac97, #else &s3c64xx_device_iisv4, #endif &samsung_asoc_dma, //LCD背光设备 &mini6410_lcd_powerdev, #ifdef CONFIG_DM9000 &s3c_device_dm9000, #endif #ifdef CONFIG_S3C_ADC &s3c_device_adc, #endif #if defined(CONFIG_TOUCHSCREEN_MINI6410) || defined(CONFIG_SAMSUNG_DEV_TS) &s3c_device_ts, #endif &s3c_device_wdt, #ifdef CONFIG_S3C_DEV_RTC &s3c_device_rtc, #endif /* Multimedia support */ #ifdef CONFIG_VIDEO_SAMSUNG &s3c_device_vpp, &s3c_device_mfc, &s3c_device_tvenc, &s3c_device_tvscaler, &s3c_device_rotator, &s3c_device_jpeg, &s3c_device_fimc0, &s3c_device_fimc1, &s3c_device_g2d, &s3c_device_g3d, #endif };
嗯,该数组里存放了很多设备,这些设备都会随着mini6410_machine_init()函数被调用而被初始化。接下来回到platform_add_devices(),它在drivers/base/platform.c里定义:
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; }
在一个for循环里通过platform_device_register()将所有定义了的平台设备逐个注册进系统。看第6行platform_device_register()的定义:
int platform_device_register(struct platform_device *pdev) { device_initialize(&pdev->dev); return platform_device_add(pdev); }
第3行的函数在上一篇已经说过,看第4行的platform_device_add():
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; }
第9行,把上一篇说过的platform_bus作为当前设备的父设备;第11行,设置当前设备所在的总线;第13~16行,pdev->id这个成员在定义平台设备时已经赋值,大部分时候被置为-1;第18~39行是对当前平台设备占有的资源(如:寄存器,中断等)报告给系统,也就是说当你要使用这个设备时要先向系统申请关于这个设备的资源,然后才可以对该设备进行操作;第44行,很熟悉了,在上一篇已经说过。
相关文章推荐
- linux驱动学习——怎么自动创建设备文件
- linux驱动学习4:scull驱动
- Linux驱动学习--初识PCI驱动(一)
- Linux内核(17) - 高效学习Linux驱动开发
- linux驱动学习(八) i2c驱动架构(史上最全) davinc dm368 i2c驱动分析【转】
- 基于mini6410的linux驱动学习总结(二 字符设备与块设备的区别)
- linux I/O内存驱动设备--学习笔记
- Linux驱动学习的最大困惑在于书籍
- 【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】Linux内核抢占实现机制分析
- 嵌入式系统学习——S3C2451之linux驱动入门
- linux驱动学习(2)-第一个驱动程序hello world
- Linux驱动学习--时间、延迟及延缓操作
- 【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】深入剖析Linux中断机制之目录
- 《Linux内核修炼之道》 之 高效学习Linux驱动开发
- 转帖(chinaunix 的creator):我的嵌入式学习之路(二) linux button 驱动
- Linux 驱动学习笔记
- 学习linux驱动经典书籍
- Linux驱动学习心得
- Linux驱动学习--时间、延迟及延缓操作2
- Linux 驱动学习