一、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);
这里就不分析具体是干什么用的了,只是提供一个分析代码的框架。