S5PV210 ADC驱动分析

本文分析了S5PV210开发板上的ADC驱动,介绍了如何利用Linux设备驱动模型中的platform虚拟总线管理ADC设备。内容涉及s3c_device_adc结构体、资源分配、设备注册和驱动匹配过程,以及s3c_adc_probe、s3c_adc_remove、s3c_adc_suspend和s3c_adc_resume等关键函数的功能。
摘要由CSDN通过智能技术生成

  我这里用的是mini210开发板,在其内核源码中,采用了Linux设备驱动模型中的platform虚拟总线来管理ADC设备。首先看S5PV210提供的ADC驱动接口。在plat-samsung/dev-adc.c中定义了s3c_device_adc,它是一个platform_device结构体,描述adc这个设备。

/* plat-samsung/dev-adc.c */
static struct resource s3c_adc_resource[] = {
    [0] = {
        .start = SAMSUNG_PA_ADC,
        .end   = SAMSUNG_PA_ADC + SZ_256 - 1,
        .flags = IORESOURCE_MEM,
    },
    [1] = {
        .start = IRQ_TC,
        .end   = IRQ_TC,
        .flags = IORESOURCE_IRQ,
    },
    [2] = {
        .start = IRQ_ADC,
        .end   = IRQ_ADC,
        .flags = IORESOURCE_IRQ,
    },
}; 
struct platform_device s3c_device_adc = {
    .name        = "samsung-adc",
    .id        = -1,
    .num_resources    = ARRAY_SIZE(s3c_adc_resource),
    .resource    = s3c_adc_resource,
};

  从中可以看出,s3c_adc_resource中定义了ADC模数转换器所需要的内存地址空间、中断等资源,然后将其作为成员放入到s3c_device_adc结构体中,接下来需要做的是将s3c_device_adc加入到Platform虚拟总线管理的设备链表中去。
  在s5pv210_map_io函数中,首先将s3c_device_adc的name属性设置成了”s5pv210-adc”。

/* mach-s5pv210/cpu.c */
void __init s5pv210_map_io(void)
{
    ...
    s3c_adc_setname("s5pv210-adc");
    ...
}
/* re-define device name depending on support. */
static inline void s3c_adc_setname(char *name)
{
**#ifdef CONFIG_SAMSUNG_DEV_ADC**
    s3c_device_adc.name = name;
**#endif**
}

  然后,s3c_device_adc结构体指针被添加到mini210_devices[]这个platform_device*结构体指针数组中。

static struct platform_device *mini210_devices[] __initdata = {
    &s3c_device_adc,
    ...
}

  最后,通过platform_add_devices(mini210_devices, ARRAY_SIZE(mini210_devices));添加s3c_device_adc到platform虚拟总线管理的设备链表中去。
在Linux的设备驱动模型中,总线、设备和驱动这3个结构体非常重要,总线将设备和驱动绑定。系统每注册一个设备的时候,就会寻找与之匹配的驱动;相反的,系统每注册一个驱动的时候,会寻找与之匹配的设备,匹配工作由总线完成。
  这里有了ADC的platform device设备,则需要对应的platform driver与其匹配,在plat-samsung/adc.c中,可以找到s3c_adc_init的定义。

static struct platform_device_id s3c_adc_driver_ids[] = {
    {
        .name       = "s3c24xx-adc",
        .driver_data    = TYPE_S3C24XX,
    }, {
        .name       = "s3c64xx-adc",
        .driver_data    = TYPE_S3C64XX,
    }, {
        .name       = "s5pv210-adc",
        .driver_data    = TYPE_S5PV210,
    },
    { }
};
MODULE_DEVICE_TABLE(platform, s3c_adc_driver_ids);

static struct platform_driver s3c_adc_driver = {
    .id_table   = s3c_adc_driver_ids,
    .driver     = {
        .name   = "s3c-adc",
        .owner  = THIS_MODULE,
    },
    .probe      = s3c_adc_probe,
    .remove     = __devexit_p(s3c_adc_remove),
    .suspend    = s3c_adc_suspend,
    .resume     = s3c_adc_resume,
};

static int __init adc_init(void)
{
    int ret;

    ret = platform_driver_register(&s3c_adc_driver);
    if (ret)
        printk(KERN_ERR "%s: failed to add adc driver\n", __func__);

    return ret;
}

arch_initcall(adc_init);

  platform_bus_type总线的math函数依靠platform_device的name属性与platform_driver的driver.name或id_table.name是否一致来完成ADC设备与ADC驱动程序的匹配,匹配成功后,则调用platform driver的probe指向的函数,来对设备进行初始化。
  s3c_adc_probe()完成了S5PV210的ADC基地址映射、分频值设置、数据位设置等,s3c_adc_remove()函数则实现了相反的功能。

static int s3c_adc_probe(struct platform_device *pdev)
{
    struct device *dev = &pdev->dev;
    struct adc_device *adc;
    struct resource *regs;
    int ret;
    unsigned tmp;

    // 为adc_device结构体分配内存
    adc
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值