一、IIC总线驱动代码

      IIC总线控制器通常是在内存上的,连接在platform总线上,所以需要通过platform_driver和platform_device的匹配。我想大概根据总线设备驱动模型的分层思想,将一个驱动程序分为device和driver两层,将IIC总线驱动程序也分成platform_device和platform_driver这两个部分来介绍。在platform_device中提供底层的硬件资源,在platform_driver中取出这些资源进行使用。可能有些不恰当,但是明确分成两部分后更容易理解

     1.1 platform_device层进行的操作

           相关的数据结构(资源、名称id、参数相关的结构体)的定义在dev-i2c0.c这个文件中  

    

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,
    },
};

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,
};

static struct s3c2410_platform_i2c default_i2c_data0 __initdata = {
    .flags        = 0,
    .slave_addr    = 0x10,
    .frequency    = 100*1000,
    .sda_delay    = 100,
};

这里对platform_device的注册不做过多的分析,接下来重点分析在platform_driver层的操作。

1.2 platform_driver层分析

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);
}
static void __exit i2c_adap_s3c_exit(void)
{
    platform_driver_unregister(&s3c24xx_i2c_driver);
}

1.2.1 platform_driver_register函数分析:

platform_driver_register(&s3c24xx_i2c_driver)

              drv->driver.bus = &platform_bus_type

              driver_register(&drv->driver)

                       driver_find(drv->name, drv->bus) //查找驱动是否已经装载如果 //没有装载则执行下边的bus_add_driver

                       bus_add_driver(drv) //根据总线类型添加对应的驱动

                        driver_add_groups(drv, drv->groups)//驱动添加到对应的组中

1.2.2 driver_find函数分析:

          kset_find_obj(bus->p->drivers_kset, name)//根据传入总线类型以//及驱动名称,在该总线类型对应的链表中寻找看该驱动是否已经存在

1.2.3 bus_add_driver函数分析:

 bus_add_driver(struct device_driver *drv)

          bus_get(drv->bus) // 获取总线类型

          klist_init(&priv->klist_devices, NULL, NULL)

          kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,"%s", drv->name)// 这些有关的kset kobject ktype等有关的数据结构等以后有空了在进行分析

          driver_attach(drv); // 因为drv->bus->p->drivers_autoprobe 一般默认的是1

                        bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);//重点分析这个函数:根据dirver所属的总线,遍历该总线的device,找到名称匹配的,在回调函数 __driver_attach中先执行

                       platform_match(struct device *dev, struct device_driver *drv)

                                   of_driver_match_device(dev, drv) //这个函数应该是一个 空函数返回0

                                    platform_match_id(pdrv->id_table, pdev) != NULL; //这里应该是这个匹配函数起了作用,从pdrv->id_table中逐个匹配设备名称"s3c2410-i2c-i2c"

匹配成功后执行,否则不执行

                     driver_probe_device(drv, dev)

                                    really_probe(dev, drv);

                                                   dev->bus->probe(dev)或者drv->probe(dev);// 因为平台总线结构体中没有探测函数,故执行驱动的探测函数即s3c24xx_i2c_probe

             klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);

             module_add_driver(drv->owner, drv);

             driver_create_file(drv, &driver_attr_uevent);

             driver_add_attrs(bus, drv);

             add_bind_files(drv);

             kobject_uevent(&priv->kobj, KOBJ_ADD);

上面是platform_driver_register函数一层一层的函数调用关系将platform_driver_register函数的作用简明扼要的说:将s3c24xx_i2c_driver这个驱动注册到platform_bus中去,在sys/bus/platform这个文件夹中穿件一些文件和文件夹(这里我们暂时不分析),并且遍历该platform_bus总线上的device设备,找到与driver_device名称匹配的设备,如果存在这种device,那么将执行probde探测函数。

1.3  s3c24xx_i2c_probe 函数分析    

        1.3.1 s3c24xx_i2c_probe 传入参数分析

  在这里有一个疑惑:传入到s3c2410_i2c_probe中的参数是一个device类型的结构体,而实际函数的参数是一个platfrom_device,我想着两个类型之间肯定有一定的关联。难道是在注册platform_device时,做了类型转换????????????????还是函数调用分析的有错误????????????

为了解决这个疑惑我们看一下这是两个成对的结构体:platform_driver 与device_driver      platform_device 与 device

  

struct platform_driver {
    int (*probe)(struct platform_device *);
    int (*remove)(struct platform_device *);
    void (*shutdown)(struct platform_device *);
    int (*suspend)(struct platform_device *, pm_message_t state);
    int (*resume)(struct platform_device *);
    struct device_driver driver;
    const struct platform_device_id *id_table;
};
struct device_driver {
    const char        *name;
    struct bus_type        *bus;

    struct module        *owner;
    const char        *mod_name;    /* used for built-in modules */

    bool suppress_bind_attrs;    /* disables bind/unbind via sysfs */

#if defined(CONFIG_OF)
    const struct of_device_id    *of_match_table;
#endif

    int (*probe) (struct device *dev);
    int (*remove) (struct device *dev);
    void (*shutdown) (struct device *dev);
    int (*suspend) (struct device *dev, pm_message_t state);
    int (*resume) (struct device *dev);
    const struct attribute_group **groups;

    const struct dev_pm_ops *pm;

    struct driver_private *p;
};


struct platform_device {
    const char    * name;
    int        id;
    struct device    dev;
    u32        num_resources;
    struct resource    * resource;

    const struct platform_device_id    *id_entry;

    /* arch specific additions */
    struct pdev_archdata    archdata;
};

struct device {
    struct device        *parent;

    struct device_private    *p;

    struct kobject kobj;
    const char        *init_name; /* initial name of the device */
    struct device_type    *type;

    struct mutex        mutex;    /* mutex to synchronize calls to
                     * its driver.
                     */

    struct bus_type    *bus;        /* type of bus device is on */
    struct device_driver *driver;    /* which driver has allocated this
                       device */
    void        *platform_data;    /* Platform specific data, device
                       core doesn't touch it */
    struct dev_pm_info    power;

#ifdef CONFIG_NUMA
    int        numa_node;    /* NUMA node this device is close to */
#endif
    u64        *dma_mask;    /* dma mask (if dma'able device) */
    u64        coherent_dma_mask;/* Like dma_mask, but for
                         alloc_coherent mappings as
                         not all hardware supports
                         64 bit addresses for consistent
                         allocations such descriptors. */

    struct device_dma_parameters *dma_parms;

    struct list_head    dma_pools;    /* dma pools (if dma'ble) */

    struct dma_coherent_mem    *dma_mem; /* internal for coherent mem
                         override */
    /* arch specific additions */
    struct dev_archdata    archdata;
#ifdef CONFIG_OF
    struct device_node    *of_node;
#endif

    dev_t            devt;    /* dev_t, creates the sysfs "dev" */

    spinlock_t        devres_lock;
    struct list_head    devres_head;

    struct klist_node    knode_class;
    struct class        *class;
    const struct attribute_group **groups;    /* optional groups */

    void    (*release)(struct device *dev);
};

注意到:

int platform_driver_register(struct platform_driver *drv)
{
    drv->driver.bus = &platform_bus_type;
    if (drv->probe)// 1
        drv->driver.probe = platform_drv_probe; // 这里有对driver_device结构体的探测函数赋值
    if (drv->remove)
        drv->driver.remove = platform_drv_remove;
    if (drv->shutdown)
        drv->driver.shutdown = platform_drv_shutdown;

    return driver_register(&drv->driver);
}


static int platform_drv_probe(struct device *_dev)     // 这里才是really_probe真正执行的探测函数
{
    struct platform_driver *drv = to_platform_driver(_dev->driver);
    struct platform_device *dev = to_platform_device(_dev);

    return drv->probe(dev);
}

然后继续看在really_probe(struct device *dev, struct device_driver *drv)中执行的是drv->probe(dev), 也就是说执行的是platform_drv_probe(struct device *_dev)就是在这个函数中实现了从device_driver变到platform_driver, device变到platform_device。

1.3.2 probe函数分析

这个函数是设备驱动函数中比较重要的一个函数,在分析这个函数之前先分析一下i2c_adapter这个结构体。在i2c-s3c2410.c中用i2c_adapter数据结构来描述一个i2c适配器(芯片内部的i2c控制器),按照我的理解:S3C6410有两个IIC控制器,则有两个i2c_adapter,而S3C2410有一个IIC控制器,则有一个i2c_adapter。

struct i2c_adapter {
    struct module *owner;
    unsigned int id __deprecated;
    unsigned int class;    //适配器所属的类     
    const struct i2c_algorithm *algo;//该总线上的通信方法这个结构体非常重要:i2c_algorithm 实际上就是具体的IIC控制器的底层操作:发出P信号S信号,中断,数据传输
    void *algo_data;    // 通信方法的附加数据
    
    struct rt_mutex bus_lock;// 对所有设备的锁结构体

    int timeout;            //超时时间
    int retries;            //重复次数
    struct device dev;    //适配器设备
i2c_adapter中封装这个结构体,说明这个适配器是作为一个设备被注册进入内核中的

    int nr;                //总线的编号
    char name[48];        //总线的名称
    struct completion dev_released;

    struct mutex userspace_clients_lock;
    struct list_head userspace_clients;
};

struct i2c_algorithm {
    
    int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,   int num);//master_xfer:对应于普通的 i2c 传输协议
    int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
               unsigned short flags, char read_write,
               u8 command, int size, union i2c_smbus_data *data);
//Smbus-xfer:i2c协议子集 smbus ,有些设备只支持这个协议
    u32 (*functionality) (struct i2c_adapter *);
//functionality 用来描述,adapter 所具有的功能,比如是否支持 smbus
};

在s3c2410_i2c_probe中要干的事有:

(1)设置i2c适配器

(2)使能i2c时钟

(3)内存映射

(4)初始化i2c控制器

(5)设置中断

(6)将i2c适配器注册到i2c总线上

static int s3c24xx_i2c_probe(struct platform_device *pdev)
{
    struct s3c24xx_i2c *i2c;
    struct s3c2410_platform_i2c *pdata;
    struct resource *res;
    int ret;
// 在platform_device中介绍了s3c_i2c0_set_platdata(...),通过这个函数我们
//将platform_data设置好,在这里将platform_data取出来使用
    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;
    }
        //1. 对IIC适配器做一些设置,尤其要注意algo关于通信方法的设置
    strlcpy(i2c->adap.name, "s3c2410-i2c", sizeof(i2c->adap.name));
    i2c->adap.owner   = THIS_MODULE;
    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 */
        //2. 使能IIC时钟
    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 */
        // 3. io内存映射
    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 */
        //4. IIC 控制器初始化
    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
     */
        // 5. 注册中断
    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;
        //6. 比较重要的一个函数,将IIC适配器注册到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);

    dev_info(&pdev->dev, "%s: S3C I2C adapter\n", dev_name(&i2c->adap.dev));
    clk_disable(i2c->clk);
    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_bus_type的driver 链表,取出每一个driver 调用 i2c_do_add_adapter

i2c_do_add_adapter

              i2c_detect(adap, driver);

                           i2c_detect_address(temp_client, driver);

                                          i2c_new_device(adapter, &info);

这里就不分析具体是干什么用的了,只是提供一个分析代码的框架。