linux IIC子系统分析(六)——I2c plaform driver 初始化

来源:互联网 发布:部落冲突亡灵升级数据 编辑:程序博客网 时间:2024/04/18 20:54

完成platform device注册之后,接下来需要初始化的是platform driver。

在driver/i2c/busees 目录下的i2c_s3c2410c 中有函数:

driver/i2c/busees/i2c_s3c2410.c

static int __init i2c_adap_s3c_init(void){return platform_driver_register(&s3c24xx_i2c_driver);      (1.0)}subsys_initcall(i2c_adap_s3c_init);
(1.0)完成platform driver的注册操作。

s3c24xx_i2c_driver 被定义为:

static struct platform_driver s3c24xx_i2c_driver = {.probe= s3c24xx_i2c_probe,.remove= s3c24xx_i2c_remove,.id_table= s3c24xx_driver_ids,    (2.0).driver= {.owner= THIS_MODULE,.name= "s3c-i2c",.pm= S3C24XX_DEV_PM_OPS,},};
(2.0)设备表赋值,其中设备表被定义为:

static struct platform_device_id s3c24xx_driver_ids[] = {{.name= "s3c2410-i2c",.driver_data= TYPE_S3C2410,}, {.name= "s3c2440-i2c",.driver_data= TYPE_S3C2440,}, { },};MODULE_DEVICE_TABLE(platform, s3c24xx_driver_ids);    (3.0)
(3.0)使用MODULE_DEVICE_TABLE 宏声明,s3c24xx_driver_ids 是platform类型的一个设备表。


platform driver 在被注册到platform bus 上的时候,会与已经注册到platform bus 上的所有platform device 进行名字配对。但是这里platform driver 有多个名字,所以就使用id_table 来实现配对。与I2C platform device相匹配的名字是:s3c2410-i2c


回到(1.0)处,我们看platform_driver_register函数,跟踪代码发现:

platform_driver_register 

driver_register

bus_add_driver

/drivers/base/Bus.c

int bus_add_driver(struct device_driver *drv){......if (drv->bus->p->drivers_autoprobe) {error = driver_attach(drv);<span style="white-space:pre"></span>(1.1)if (error)goto out_unregister;}<span style="white-space:pre"></span>......if (!drv->suppress_bind_attrs) {error = add_bind_files(drv);<span style="white-space:pre"></span>(1.2)if (error) {/* Ditto */printk(KERN_ERR "%s: add_bind_files(%s) failed\n",__func__, drv->name);}}<span style="white-space:pre"></span>......}

(1.1)进行platform device与platform driver 的配对

(1.2)进行platform device 与platform driver 的绑定

在绑定函数中:

static int __must_check add_bind_files(struct device_driver *drv){int ret;ret = driver_create_file(drv, &driver_attr_unbind);if (ret == 0) {ret = driver_create_file(drv, &driver_attr_bind);    (1.3)if (ret)driver_remove_file(drv, &driver_attr_unbind);}return ret;}
(1.3) 这里会调用dirver_bind函数:

static ssize_t driver_bind(struct device_driver *drv,   const char *buf, size_t count){struct bus_type *bus = bus_get(drv->bus);struct device *dev;int err = -ENODEV;dev = bus_find_device_by_name(bus, NULL, buf);if (dev && dev->driver == NULL && driver_match_device(drv, dev)) {if (dev->parent)/* Needed for USB */down(&dev->parent->sem);down(&dev->sem);err = driver_probe_device(drv, dev);           (1.5)up(&dev->sem);if (dev->parent)up(&dev->parent->sem);if (err > 0) {/* success */err = count;} else if (err == 0) {/* driver didn't accept device */err = -ENODEV;}}put_device(dev);bus_put(bus);return err;}static DRIVER_ATTR(bind, S_IWUSR, NULL, driver_bind);       (1.4)
(1.4)DRIVER_ATTR 的定义为下面的这个宏,这也是为什么(1.3)处会调用到driver_bind 函数。

#define DRIVER_ATTR(_name, _mode, _show, _store)        \struct driver_attribute driver_attr_##_name =           \        __ATTR(_name, _mode, _show, _store)

(1.5)进一步分析probe函数:

driver_probe_device

really_probe

static int really_probe(struct device *dev, struct device_driver *drv){int ret = 0;atomic_inc(&probe_count);pr_debug("bus: '%s': %s: probing driver %s with device %s\n", drv->bus->name, __func__, drv->name, dev_name(dev));WARN_ON(!list_empty(&dev->devres_head));dev->driver = drv;if (driver_sysfs_add(dev)) {printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",__func__, dev_name(dev));goto probe_failed;}if (dev->bus->probe) {<span style="white-space:pre"></span>(1.6)ret = dev->bus->probe(dev);if (ret)goto probe_failed;} else if (drv->probe) {ret = drv->probe(dev);<span style="white-space:pre"></span>(1.7)if (ret)goto probe_failed;}driver_bound(dev);<span style="white-space:pre"></span>......}
在(1.6)我们的bus->probe 是没有提供probe 方法的,因此调用的是下面(1.7)drv->probe(dev),也就是s3c24xx_i2c_probe 函数:

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;                   (5.0)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;}strlcpy(i2c->adap.name, "s3c2410-i2c", sizeof(i2c->adap.name));i2c->adap.owner   = THIS_MODULE;i2c->adap.algo    = &s3c24xx_i2c_algorithm;     (6.0)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);                 (7.0)/* 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);                       (8.0)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,  (9.0)  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;ret = i2c_add_numbered_adapter(&i2c->adap);              (1.0)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));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;}

(5.0)获取s3c24xx_i2c.platform_data 里面i2c的一些设备参数,也就是在platform device 注册到内核的那些设备信息。

(6.0)初始化algo 算法。

(7.0)初始化一个等待队列

(8.0)初始化i2c 控制器,主要是针对s3c24xx_i2c 寄存器的一些操作。

(9.0)申请中断,内核中i2c的读写通过中断来实现。

(10)向系统注册一个i2c adapter


初始化到这里,IIC总线设备已经初始化完成。也就是I2C硬件体系结构中适配器端的驱动程序已经完成。

如果需要调用I2C子系统去访问客户端设备,这里还需要添加I2C设备驱动(客户驱动)


说明:

1.分析的内核版本是linux2.6.32.2

2.开发板为友善之臂的mini2440, 用的是ARM9(S3C2440A)处理器

3.链接的IIC设备是EEPROM(AT24C02)

4.按照内核I2C子系统的注册顺序分析。




0 0