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

完成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子系统的注册顺序分析。

  

---------------------------------------2022.08.20:21:20更新----------------------------------------

由于各种原因,后续文章内容将更新到公众号,本平台将不再做更新。

CSDN上相关文章的测试工程代码,也统一放到了公众号上,可以免费免积分下载

可以通过主页上的二维码,也可以通过搜索微信公众号 liwen01 进入公众号

liwen01   2022.08.20

---------------------------------------2022.08.20:21:20更新----------------------------------------


 

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

li_wen01

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值