I2C设备驱动框架和一个实例注册

来源:互联网 发布:千牛淘宝卖家版 编辑:程序博客网 时间:2024/05/16 12:03

注:这是我在自己学习I2C驱动框架时候记录的笔记,不喜勿喷
一、内核中实现I2C驱动框架和一个实例注册相关的文件
1./dricer/i2c/i2c-core.c
2./arch/arm/mach-s3c2440/mach-smdk2440.c
3.driver/i2c/buses/i2c-s3c2410.c
4./driver/i2c/i2c-dev.c
以后分析的代码就来源于这几个文件中

二、几个相关的结构体
1.相关的结构体
(1)struct i2c_adapter 主芯片的I2C适配器
(2)struct i2c_algorthm I2C算法
(3)struct i2c_client I2C(从设备)客户端(信息)
(4)struct i2c_driver I2C(从设备)驱动

struct i2c_adapter {    struct module *owner;    //id号    unsigned int id;    unsigned int class;       /* classes to allow probing for */    //I2C通信相关的算法。注意是指针,也就是说一个adapter可以使用多种I2C通信算法    const struct i2c_algorithm *algo; /* the algorithm to access the bus */    //算法相关数据    void *algo_data;    /* data fields that are valid for all devices   */    //I2C总线相关的互斥锁    struct rt_mutex bus_lock;    int timeout;            /* in jiffies */    //尝试重新连接的发送数据的次数    int retries;    struct device dev;      /* the adapter device */    int nr;    //名字    char name[48];    struct completion dev_released;    struct list_head userspace_clients;};
struct i2c_algorithm {    //发送数据的函数指针    int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,               int num);    int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,               unsigned short flags, char read_write,               u8 command, int size, union i2c_smbus_data *data);    /* To determine what the adapter supports */    //决定支持什么类型的adapter的函数指针    u32 (*functionality) (struct i2c_adapter *);};
struct i2c_client {    unsigned short flags;       /* div., see below      */    //低7位的I2C设备地址    unsigned short addr;    //名字        char name[I2C_NAME_SIZE];    //相对的adapter    struct i2c_adapter *adapter;    //相对的驱动    struct i2c_driver *driver;  /* and our access routines  */    //实体I2C设备    struct device dev;      /* the device structure     */    //中断号    int irq;            /* irq issued by device     */    struct list_head detected;};
//i2C相关驱动所需的函数指针的封装struct i2c_driver {    unsigned int class;    /* Notifies the driver that a new bus has appeared or is about to be     * removed. You should avoid using this if you can, it will probably     * be removed in a near future.     */    int (*attach_adapter)(struct i2c_adapter *);    int (*detach_adapter)(struct i2c_adapter *);    /* Standard driver model interfaces */    int (*probe)(struct i2c_client *, const struct i2c_device_id *);    int (*remove)(struct i2c_client *);    /* driver model interfaces that don't relate to enumeration  */    void (*shutdown)(struct i2c_client *);    int (*suspend)(struct i2c_client *, pm_message_t mesg);    int (*resume)(struct i2c_client *);    /* Alert callback, for example for the SMBus alert protocol.     * The format and meaning of the data value depends on the protocol.     * For the SMBus alert protocol, there is a single bit of data passed     * as the alert response's low bit ("event flag").     */    void (*alert)(struct i2c_client *, unsigned int data);    /* a ioctl like command that can be used to perform specific functions     * with the device.     */    int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);    struct device_driver driver;    //用来machdevice和driver的idtable    const struct i2c_device_id *id_table;    /* Device detection callback for automatic device creation */    int (*detect)(struct i2c_client *, struct i2c_board_info *);    const unsigned short *address_list;    struct list_head clients;};

三、I2C框架的注册

struct bus_type i2c_bus_type = {    .name       = "i2c",    .match      = i2c_device_match,    .probe      = i2c_device_probe,    .remove     = i2c_device_remove,    .shutdown   = i2c_device_shutdown,    .pm     = &i2c_device_pm_ops,};
static int __init i2c_init(void){    int retval;    //在BUS总线下注册I2C总线    retval = bus_register(&i2c_bus_type);    if (retval)        return retval;#ifdef CONFIG_I2C_COMPAT    //在class下面创建"i2c-adapter"目录    i2c_adapter_compat_class = class_compat_register("i2c-adapter");    if (!i2c_adapter_compat_class) {        retval = -ENOMEM;        goto bus_err;    }#endif    //添加一个dummy_driver,具体没看代码    retval = i2c_add_driver(&dummy_driver);    if (retval)        goto class_err;    return 0;class_err:#ifdef CONFIG_I2C_COMPAT    class_compat_unregister(i2c_adapter_compat_class);bus_err:#endif    bus_unregister(&i2c_bus_type);    return retval;}

框架注册总结:实际在BUS总线下注册了I2C总线,重要的还是2c_bus_type结构体,具体我们看两个重要的函数指针.match和.prob。

//mach函数用来匹配client和driver,具体的匹配方式是通过driver中的idtable和client中的name来匹配的。具体函数看i2c_match_id(driver->id_table, client)static int i2c_device_match(struct device *dev, struct device_driver *drv){    struct i2c_client   *client = i2c_verify_client(dev);    struct i2c_driver   *driver;    if (!client)        return 0;    driver = to_i2c_driver(drv);    /* match on an id table if there is one */    if (driver->id_table)        return i2c_match_id(driver->id_table, client) != NULL;    return 0;}
static int i2c_device_probe(struct device *dev){    struct i2c_client   *client = i2c_verify_client(dev);    struct i2c_driver   *driver;    int status;    if (!client)        return 0;    driver = to_i2c_driver(dev->driver);    //检验driver中是否提供prob和id_table    if (!driver->probe || !driver->id_table)        return -ENODEV;    //client的driver和驱动关联    client->driver = driver;    if (!device_can_wakeup(&client->dev))        device_init_wakeup(&client->dev,                    client->flags & I2C_CLIENT_WAKE);    dev_dbg(dev, "probe\n");    //调用驱动中写的driver,这个函数是我们自己实现的prob    status = driver->probe(client, i2c_match_id(driver->id_table, client));    if (status) {        client->driver = NULL;        i2c_set_clientdata(client, NULL);    }    return status;}

注册到这里就完成了,到时候我们自己写的驱动和设备mach之后会自动调用prob函数执行。
i2c-core.c中提供了一些驱动工程师可以调用的接口来帮助写设备驱动,这些接口在用到的时候会列出源代码

四、实例分析
1.我们先看i2c的device的注册
(1)具体的代码在mach-xxx.c里面
这里只列出相关的函数调用(并且只举例了一个i2c0)

smdkc110_machine_init    platform_add_devices(smdkc110_devices, ARRAY_SIZE(smdkc110_devices));        platform_device_register(&s3c_device_i2c0);    s3c_i2c0_set_platdata(NULL);    i2c_register_board_info(0, i2c_devs0, ARRAY_SIZE(i2c_devs0));
/*******************相关注册device的结构体**********************/static struct s3c2410_platform_i2c default_i2c_data0 __initdata = {    .flags      = 0,    .slave_addr = 0x10,    .frequency  = 400*1000,    .sda_delay  = S3C2410_IICLC_SDA_DELAY15 | S3C2410_IICLC_FILTER_ON,};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,    },};//这里的name必须和driver里面的id_table的name一致struct platform_device s3c_device_i2c0 = {    .name         = "s3c2410-i2c",    .id       = 0,    .num_resources    = ARRAY_SIZE(s3c_i2c_resource),    .resource     = s3c_i2c_resource,};/*******************相关注册device的结构体**********************///传参为NULLvoid __init s3c_i2c0_set_platdata(struct s3c2410_platform_i2c *pd){    struct s3c2410_platform_i2c *npd;    if (!pd)        pd = &default_i2c_data0;    npd = kmemdup(pd, sizeof(struct s3c2410_platform_i2c), GFP_KERNEL);    if (!npd)        printk(KERN_ERR "%s: no memory for platform data\n", __func__);    else if (!npd->cfg_gpio)        npd->cfg_gpio = s3c_i2c0_cfg_gpio;    s3c_device_i2c0.dev.platform_data = npd;}
static struct i2c_board_info i2c_devs0[] __initdata = {#ifdef CONFIG_SND_SOC_WM8580    {        I2C_BOARD_INFO("wm8580", 0x1b),    },#endif};int __init i2c_register_board_info(int busnum, struct i2c_board_info const *info, unsigned len){    int status;    down_write(&__i2c_board_lock);    /* dynamic bus numbers will be assigned after the last static one */    if (busnum >= __i2c_first_dynamic_bus_num)        __i2c_first_dynamic_bus_num = busnum + 1;    for (status = 0; len; len--, info++) {        struct i2c_devinfo  *devinfo;        devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL);        if (!devinfo) {            pr_debug("i2c-core: can't register boardinfo!\n");            status = -ENOMEM;            break;        }        devinfo->busnum = busnum;        devinfo->board_info = *info;        //最重要的就是这句,把devinfo注册到__i2c_board_list中,到时候在注册adapter的时候才能找到I2C设备        list_add_tail(&devinfo->list, &__i2c_board_list);    }    up_write(&__i2c_board_lock);    return status;}

到这I2C设备注册也就结束了,相关结构体之间的关联,我懒得画图,我也是借用大神的图来参考的。
这里写图片描述
这里写图片描述
这两张图片对应上面的函数,可以自己分析。

五、驱动实例的分析
1.参考i2c-s3c2410.c来分析,它采用的平台驱动模型来写的I2C驱动

static struct platform_device_id s3c24xx_driver_ids[] = {    {        .name       = "s3c2410-i2c",        .driver_data    = TYPE_S3C2410,    }, {        .name       = "s3c2440-i2c",        .driver_data    = TYPE_S3C2440,    }, { },};static struct platform_driver s3c24xx_i2c_driver = {    .probe      = s3c24xx_i2c_probe,    .remove     = s3c24xx_i2c_remove,    .id_table   = s3c24xx_driver_ids,    .driver     = {        .owner  = THIS_MODULE,        .name   = "s3c-i2c",        .pm = S3C24XX_DEV_PM_OPS,    },};static int __init i2c_adap_s3c_init(void){    return platform_driver_register(&s3c24xx_i2c_driver);}

我们知道在platform平台驱动,中只要driver和device相遇匹配之后就会执行prob函数。所以接下来我们看prob函数

static int s3c24xx_i2c_probe(struct platform_device *pdev){    struct s3c24xx_i2c *i2c;    struct s3c2410_platform_i2c *pdata;    struct resource *res;    int ret;    pdata = pdev->dev.platform_data;    if (!pdata) {        dev_err(&pdev->dev, "no platform data\n");        return -EINVAL;    }    i2c = kzalloc(sizeof(struct s3c24xx_i2c), GFP_KERNEL);    if (!i2c) {        dev_err(&pdev->dev, "no memory for state\n");        return -ENOMEM;    }    //设置I2C的名字    strlcpy(i2c->adap.name, "s3c2410-i2c", sizeof(i2c->adap.name));    i2c->adap.owner   = THIS_MODULE;    //填充I2C中adap的通信算法,这个算法我不太懂。    i2c->adap.algo    = &s3c24xx_i2c_algorithm;    //填充重连次数    i2c->adap.retries = 2;    i2c->adap.class   = I2C_CLASS_HWMON | I2C_CLASS_SPD;    i2c->tx_setup     = 50;    spin_lock_init(&i2c->lock);    init_waitqueue_head(&i2c->wait);    /* find the clock and enable it */    i2c->dev = &pdev->dev;    i2c->clk = clk_get(&pdev->dev, "i2c");    if (IS_ERR(i2c->clk)) {        dev_err(&pdev->dev, "cannot get clock\n");        ret = -ENOENT;        goto err_noclk;    }    dev_dbg(&pdev->dev, "clock source %p\n", i2c->clk);    clk_enable(i2c->clk);    /* map the registers */    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);    if (res == NULL) {        dev_err(&pdev->dev, "cannot find IO resource\n");        ret = -ENOENT;        goto err_clk;    }    i2c->ioarea = request_mem_region(res->start, resource_size(res),                     pdev->name);    if (i2c->ioarea == NULL) {        dev_err(&pdev->dev, "cannot request IO\n");        ret = -ENXIO;        goto err_clk;    }    i2c->regs = ioremap(res->start, resource_size(res));    if (i2c->regs == NULL) {        dev_err(&pdev->dev, "cannot map IO\n");        ret = -ENXIO;        goto err_ioarea;    }    dev_dbg(&pdev->dev, "registers %p (%p, %p)\n",        i2c->regs, i2c->ioarea, res);    /* setup info block for the i2c core */    //相互关联    i2c->adap.algo_data = i2c;    i2c->adap.dev.parent = &pdev->dev;    /* initialise the i2c controller */    //具体的寄存器配置就在这个函数里面    ret = s3c24xx_i2c_init(i2c);    if (ret != 0)        goto err_iomap;    /* find the IRQ for this unit (note, this relies on the init call to     * ensure no current IRQs pending     */    i2c->irq = ret = platform_get_irq(pdev, 0);    if (ret <= 0) {        dev_err(&pdev->dev, "cannot find IRQ\n");        goto err_iomap;    }    ret = request_irq(i2c->irq, s3c24xx_i2c_irq, IRQF_DISABLED,              dev_name(&pdev->dev), i2c);    if (ret != 0) {        dev_err(&pdev->dev, "cannot claim IRQ %d\n", i2c->irq);        goto err_iomap;    }    ret = s3c24xx_i2c_register_cpufreq(i2c);    if (ret < 0) {        dev_err(&pdev->dev, "failed to register cpufreq notifier\n");        goto err_irq;    }    /* Note, previous versions of the driver used i2c_add_adapter()     * to add the bus at any number. We now pass the bus number via     * the platform data, so if unset it will now default to always     * being bus 0.     */    i2c->adap.nr = pdata->bus_num;    //这个函数才是最重要的,也是i2c-core.c提供的接口,来注册i2c设备。    ret = i2c_add_numbered_adapter(&i2c->adap);    if (ret < 0) {        dev_err(&pdev->dev, "failed to add bus to i2c core\n");        goto err_cpufreq;    }    platform_set_drvdata(pdev, i2c);    clk_disable(i2c->clk);    dev_info(&pdev->dev, "%s: S3C I2C adapter\n", dev_name(&i2c->adap.dev));    return 0; err_cpufreq:    s3c24xx_i2c_deregister_cpufreq(i2c); err_irq:    free_irq(i2c->irq, i2c); err_iomap:    iounmap(i2c->regs); err_ioarea:    release_resource(i2c->ioarea);    kfree(i2c->ioarea); err_clk:    clk_disable(i2c->clk);    clk_put(i2c->clk); err_noclk:    kfree(i2c);    return ret;}

调用层次(注意:从这里开始就是i2c-core.c提供的接口了)

i2c_add_numbered_adapter    i2c_register_adapter
static int i2c_register_adapter(struct i2c_adapter *adap){    int res = 0, dummy;    /* Can't register until after driver model init */    if (unlikely(WARN_ON(!i2c_bus_type.p))) {        res = -EAGAIN;        goto out_list;    }    rt_mutex_init(&adap->bus_lock);    INIT_LIST_HEAD(&adap->userspace_clients);    /* Set default timeout to 1 second if not already set */    if (adap->timeout == 0)        adap->timeout = HZ;    //设置dev的名字    dev_set_name(&adap->dev, "i2c-%d", adap->nr);    //设置dev.bus    adap->dev.bus = &i2c_bus_type;    //设置dev.type    adap->dev.type = &i2c_adapter_type;    //注册adap->dev    res = device_register(&adap->dev);    if (res)        goto out_list;    dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name);#ifdef CONFIG_I2C_COMPAT    res = class_compat_create_link(i2c_adapter_compat_class, &adap->dev,                       adap->dev.parent);    if (res)        dev_warn(&adap->dev,             "Failed to create compatibility class link\n");#endif    /* create pre-declared device nodes */    //重要点。根据分析会调用i2c_scan_static_board_info函数    if (adap->nr < __i2c_first_dynamic_bus_num)        i2c_scan_static_board_info(adap);    /* Notify drivers */    mutex_lock(&core_lock);    //找到相应的driver对应adapter    dummy = bus_for_each_drv(&i2c_bus_type, NULL, adap,                 __process_new_adapter);    mutex_unlock(&core_lock);    return 0;out_list:    mutex_lock(&core_lock);    idr_remove(&i2c_adapter_idr, adap->nr);    mutex_unlock(&core_lock);    return res;}
static void i2c_scan_static_board_info(struct i2c_adapter *adapter){    struct i2c_devinfo  *devinfo;    down_read(&__i2c_board_lock);    //注意__i2c_board_list链表在I2C设备注册时候就已经填充了,接着调用i2c_new_device函数    list_for_each_entry(devinfo, &__i2c_board_list, list) {        if (devinfo->busnum == adapter->nr                && !i2c_new_device(adapter,                        &devinfo->board_info))            dev_err(&adapter->dev,                "Can't create device at 0x%02x\n",                devinfo->board_info.addr);    }    up_read(&__i2c_board_lock);}
//这个函数作用:创建并且填充client,还绑定了相对的adapterstruct i2c_client *i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info){    struct i2c_client   *client;    int         status;    client = kzalloc(sizeof *client, GFP_KERNEL);    if (!client)        return NULL;    client->adapter = adap;    client->dev.platform_data = info->platform_data;    if (info->archdata)        client->dev.archdata = *info->archdata;    client->flags = info->flags;    client->addr = info->addr;    client->irq = info->irq;    strlcpy(client->name, info->type, sizeof(client->name));    /* Check for address validity */    status = i2c_check_client_addr_validity(client);    if (status) {        dev_err(&adap->dev, "Invalid %d-bit I2C address 0x%02hx\n",            client->flags & I2C_CLIENT_TEN ? 10 : 7, client->addr);        goto out_err_silent;    }    /* Check for address business */    status = i2c_check_addr_busy(adap, client->addr);    if (status)        goto out_err;    client->dev.parent = &client->adapter->dev;    client->dev.bus = &i2c_bus_type;    client->dev.type = &i2c_client_type;#ifdef CONFIG_OF    client->dev.of_node = info->of_node;#endif    dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap),             client->addr);    //在这里device_register在I2C总线下添加了client    status = device_register(&client->dev);    if (status)        goto out_err;    dev_dbg(&adap->dev, "client [%s] registered with bus id %s\n",        client->name, dev_name(&client->dev));    return client;out_err:    dev_err(&adap->dev, "Failed to register i2c client %s at 0x%02x "        "(%d)\n", client->name, client->addr, status);out_err_silent:    kfree(client);    return NULL;}
bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter);    //这里的参数是简化过的,自己分析下就明白了    __process_new_adapter(drv, data);        i2c_do_add_adapter(to_i2c_driver(d), data);
static int i2c_do_add_adapter(struct i2c_driver *driver,                  struct i2c_adapter *adap){    //这是老版本使用的,所以在这里直接返回了。    i2c_detect(adap, driver);    /* Let legacy drivers scan this bus for matching devices */    //driver->attach_adapter函数指针在i2c-dev.c里面被赋值    if (driver->attach_adapter) {        /* We ignore the return code; if it fails, too bad */        driver->attach_adapter(adap);    }    return 0;}

到这里基本的初始化已经完成了。
这里写图片描述

0 0
原创粉丝点击