platform驱动详解

platform总线是虚拟的平台总线,是linux设备驱动模型为了保持设备驱动的统一性而虚拟出来的总线。
总线将设备和驱动绑定,系统每注册一个设备的时候,会寻找与之匹配的驱动;相反,系统每注册一个驱动的时候,会寻找与之匹配的设备,而匹配由总线完成。
platform驱动工作流程:
1. 系统开机内核初始化阶段,初始化platform总线;
2. platform_device初始化调用platform_add_devices(一次注册多个设备)或者platform_device_register(一次注册单个设备),这部分一般在arch/arm/mach配置文件中,在上电开机的时候完成platform_device初始化
3. platform_driver的初始化调用platform_driver_register或者driver_register,该过程一般在驱动程序的init函数中

platform总线的初始化

platform总线的初始化是在内核的初始化阶段完成的
整个流程是kernel_init() –> do_basic_setup() –> driver_init() –> platform_bus_init()
下面分析platform_bus_init() 函数

struct device platform_bus = {  /*定义一个名为platform的总线设备*/
    .init_name  = "platform",   /*其他的platform设备都是它的子设备*/
};

struct bus_type platform_bus_type = {
    .name       = "platform",
    .dev_attrs  = platform_dev_attrs,
    .match      = platform_match,
    .uevent     = platform_uevent,
    .pm     = &platform_dev_pm_ops,
};
EXPORT_SYMBOL_GPL(platform_bus_type);

int __init platform_bus_init(void)
{
    int error;

    early_platform_cleanup();
    /* platform总线也是设备,对应的sysfs节点为/sys/devices/platform */
    error = device_register(&platform_bus);
    if (error)
        return error;
    error =  bus_register(&platform_bus_type);//注册平台类型的bus,将出现sys文  件系统在bus目录下,创建一个platform的目录,以及相关属性文件 
    if (error)
        device_unregister(&platform_bus);
    return error;
}

注册platform_device

platform_device的注册一般放在arch/arm/mach配置文件中,在开机时被初始化,所以当注册platform_driver时会在platform总线上查找匹配的设备

struct platform_device {
 const char * name;     /* 设备名 */
 u32 id;        //当有多个设备用来 区分 ,只有一个设备,id=-1
 struct device dev;
  struct resource    *  resource ;      /* 设备所使用各类资源数量 */
 struct resource * resource; /* 资源 */
 };

这里有个重要的成员resource???该结构存入了资源信息

struct resource {
    resource_size_t start;
    resource_size_t end;
    const char *name;
    unsigned long flags;
    struct resource *parent, *sibling, *child;
};

下面举 s3c6410 平台的 i2c 驱动作为例子来说明platform_device的注册过程:

static struct platform_device *smdk6410_devices [] __initdata = {
#ifdef CONFIG_SMDK6410_SD_CH0
    &s3c_device_hsmmc0,
#endif
#ifdef CONFIG_SMDK6410_SD_CH1
    &s3c_device_hsmmc1,
#endif
    &s3c_device_i2c0 ,
    &s3c_device_i2c1,
    &s3c_device_fb,
    &s3c_device_usb,
    &s3c_device_usb_hsotg,
    &smdk6410_lcd_powerdev,
    &smdk6410_smsc911x,
};

把一个或几个设备资源放在一起,便于集中管理,其中IIC设备 platform_device如下:

struct platform_device  s3c_device_i2c0  = {
    .name          = "s3c2410-i2c",
#ifdef CONFIG_S3C_DEV_I2C1
    .id          = 0,
#else
    .id          = -1,
#endif
    .num_resources      = ARRAY_SIZE(s3c_i2c_resource ),
    .resource      = s3c_i2c_resource,
};

具体resource如下:
static struct resource  s3c_i2c_resource [] = {
    [0] = {
        .start = S3C_PA_IIC,
        .end   = S3C_PA_IIC + SZ_4K - 1,
        .flags = IORESOURCE_MEM,
    },
    [1] = {
        .start = IRQ_IIC,
        .end   = IRQ_IIC,
        .flags = IORESOURCE_IRQ,
    },
};

这里定义了两组 resource ,它描述了一个 I2C 设备的资源,第 1 组描述了这个 I2C 设备所占用的总线地址范围, IORESOURCE_MEM 表示第 1 组描述的是内存类型的资源信息,第 2 组描述了这个 I2C 设备的中断号,IORESOURCE_IRQ 表示第 2 组描述的是中断资源信息。设备驱动会根据 flags 来获取相应的资源信息。

定义好了platform_device 结构体后就可以调用函数 platform_add_devices向系统中添加该设备了,之后可以调用 platform_driver_register() 进行设备注册。
s3c6410-i2cplatform_device是在系统启动时,在mach-smdk6410.c里的smdk6410_machine_init()函数里进行注册的,这个函数申明为arch_initcall的函数调用,arch_initcall的优先级高于module_init。所以会在Platform驱动注册之前调用。(详细参考imach-smdk6410.c)

static void __init smdk6410_machine_init(void)
{
    s3c_i2c0_set_platdata(NULL);
    s3c_i2c1_set_platdata(NULL);
    s3c_fb_set_platdata(&smdk6410_lcd_pdata);
    gpio_request(S3C64XX_GPN(5), "LCD power");
    gpio_request(S3C64XX_GPF(13), "LCD power");
    gpio_request(S3C64XX_GPF(15), "LCD power");
    i2c_register_board_info(0, i2c_devs0, ARRAY_SIZE(i2c_devs0));
    i2c_register_board_info(1, i2c_devs1, ARRAY_SIZE(i2c_devs1));
    platform_add_devices(smdk6410_devices, ARRAY_SIZE(smdk6410_devices));
    //添加多设备
}

下面分析一下platform_add_devices是如何注册platform_device的:
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)
         //如果有id 表示有多个同类设备用 pdev->name和 pdev->id标识该设备
  dev_set_name(&pdev->dev, "%s.%d", pdev->name,  pdev->id);
    else
        dev_set_name(&pdev->dev, "%s", pdev->name);
 //否则,只用 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; //   作为 IOMEM 资源分配  
            else if (resource_type(r) == IORESOURCE_IO)
                 p = &ioport_resource; //   作为 IO PORT资源分配  
        }
         if (p && insert_resource(p, r)) { // 将新的 resource 插入内核 resource tree
            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;
}

platform_driver注册

platform_driver结构体初始化,以i2c为例

static struct platform_driver s3c24xx_i2c_driver = {
    .probe        = s3c24xx_i2c_probe,
    .remove        = s3c24xx_i2c_remove,
    .suspend_late    = s3c24xx_i2c_suspend_late,
    .resume        = s3c24xx_i2c_resume,
    .id_table    = s3c24xx_driver_ids,
    .driver        = {
        .owner    = THIS_MODULE,
        .name    = "s3c-i2c",
    },
};

在驱动程序的初始化函数中调用platform_driver_add()注册platform_driver,中线的匹配函数(platrorm_mach)会匹配总线上已经存在的设备的名称(name字段),当设备与驱动匹配成功,调用驱动程序中的probe函数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值