您的位置:首页 > 运维架构 > Linux

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()函数是什么时候被调用的,因此在这里不再重复,直接看这个函数里面的内容:

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行,很熟悉了,在上一篇已经说过。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: