I2C总线(二)注册控制器

一、i2c适配器

i2c适配器在硬件层面其实就是i2c控制器,因为跟芯片相关,一般内核会带对应厂商的芯片驱动,实现在i2c/busses中找好了。

我们直接看代码,以imx6为例。

1、平台总线匹配

imx6中是做了,驱动和设备树分离,以平台总线实现,
我们主要关注点是:

1、设备树中,找到有设备,节点为i2c1,
I2C总线(二)注册控制器_第1张图片
2、平台驱动中有定义驱动,并且我们看到 compatible 可以匹配上,
那么将,执行 i2c_imx_probe 。。。
I2C总线(二)注册控制器_第2张图片
I2C总线(二)注册控制器_第3张图片

2、执行probe(i2c_imx_probe),构建适配器

下面给这段代码做注释

static int i2c_imx_probe(struct platform_device *pdev)
{
	const struct of_device_id *of_id = of_match_device(i2c_imx_dt_ids,
							   &pdev->dev);
	struct imx_i2c_struct *i2c_imx;
	struct resource *res;
	struct imxi2c_platform_data *pdata = dev_get_platdata(&pdev->dev);
	void __iomem *base;
	int irq, ret;
	u32 bitrate;

	dev_dbg(&pdev->dev, "<%s>\n", __func__);

	// 设备树定义了中断,获取中断
	irq = platform_get_irq(pdev, 0);
	if (irq < 0) {
		dev_err(&pdev->dev, "can't get irq number\n");
		return irq;
	}

	// 获取寄存器地址,并且映射成虚拟地址
	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	base = devm_ioremap_resource(&pdev->dev, res);
	if (IS_ERR(base))
		return PTR_ERR(base);

	// 分配内存,imx_i2c_struct,是imx6自定义数据结构,
	// 但是注意,内部有一个 i2c_adapter,是属于i2c核心的数据结构
	// i2c_adapter 将会被注册到核心
	i2c_imx = devm_kzalloc(&pdev->dev, sizeof(struct imx_i2c_struct),
				GFP_KERNEL);
	if (!i2c_imx) {
		dev_err(&pdev->dev, "can't allocate interface\n");
		return -ENOMEM;
	}

	// 
	if (of_id)
		i2c_imx->hwdata = of_id->data;
	else
		i2c_imx->hwdata = (struct imx_i2c_hwdata *)
				platform_get_device_id(pdev)->driver_data;



	// 下面是对重要结构体 i2c_adapter 初始化
	// 
	// 1、这里将平台设备的名称,给 i2c_adapter 赋值了,所以他们的名字将一样
	/* Setup i2c_imx driver structure */
	strlcpy(i2c_imx->adapter.name, pdev->name, sizeof(i2c_imx->adapter.name));
	// 2、
	i2c_imx->adapter.owner		= THIS_MODULE;
	// 3、给适配器初始化了一个算法,
	// 通过这个回调,i2c核心将可以对控制器做硬件操作(收发数据)
	i2c_imx->adapter.algo		= &i2c_imx_algo;
	// 4、
	i2c_imx->adapter.dev.parent	= &pdev->dev;
	// 5、设备树定义的编号(i2c1的1),给到对应的适配器
	i2c_imx->adapter.nr 		= pdev->id;
	// 6、设备树节点获取到了
	i2c_imx->adapter.dev.of_node	= pdev->dev.of_node;
	// 7、imx6自己保存的,寄存器的基地址(虚拟地址)
	i2c_imx->base			= base;



	// 获取时钟(设备树定义的)
	/* Get I2C clock */
	i2c_imx->clk = devm_clk_get(&pdev->dev, NULL);
	if (IS_ERR(i2c_imx->clk)) {
		dev_err(&pdev->dev, "can't get I2C clock\n");
		return PTR_ERR(i2c_imx->clk);
	}
	// 使能时钟(硬件操作)
	ret = clk_prepare_enable(i2c_imx->clk);
	if (ret) {
		dev_err(&pdev->dev, "can't enable I2C clock\n");
		return ret;
	}



	// 请求中断
	/* Request IRQ */
	ret = devm_request_irq(&pdev->dev, irq, i2c_imx_isr, 0,
				pdev->name, i2c_imx);
	if (ret) {
		dev_err(&pdev->dev, "can't claim irq %d\n", irq);
		return ret;
	}

	// 初始化队列
	/* Init queue */
	init_waitqueue_head(&i2c_imx->queue);

	// 这里注意,把imx6自定义数据给了 i2c_adapter 的私有数据。。
	/* Set up adapter data */
	i2c_set_adapdata(&i2c_imx->adapter, i2c_imx);

	// 设置时钟的频率(硬件操作)
	/* Set up clock divider */
	bitrate = IMX_I2C_BIT_RATE;
	ret = of_property_read_u32(pdev->dev.of_node,
				   "clock-frequency", &bitrate);
	if (ret < 0 && pdata && pdata->bitrate)
		bitrate = pdata->bitrate;
	i2c_imx_set_clk(i2c_imx, bitrate);

	//
	/* Set up chip registers to defaults */
	imx_i2c_write_reg(i2c_imx->hwdata->i2cr_ien_opcode ^ I2CR_IEN,
			i2c_imx, IMX_I2C_I2CR);
	imx_i2c_write_reg(i2c_imx->hwdata->i2sr_clr_opcode, i2c_imx, IMX_I2C_I2SR);


	// 重中之重,,,i2c_adapter 被注册,
	// 调用的是 i2c_add_numbered_adapter,是在i2c 核心定义的接口。。
	// 如果要了解该接口,移步 i2c 核心章节。。
	/* Add I2C adapter */
	ret = i2c_add_numbered_adapter(&i2c_imx->adapter);
	if (ret < 0) {
		dev_err(&pdev->dev, "registration failed\n");
		return ret;
	}

	// imx6自定义的数据结构体,也成了平台设备的私有数据了
	/* Set up platform driver data */
	platform_set_drvdata(pdev, i2c_imx);
	clk_disable_unprepare(i2c_imx->clk);

	dev_dbg(&i2c_imx->adapter.dev, "claimed irq %d\n", irq);
	dev_dbg(&i2c_imx->adapter.dev, "device resources from 0x%x to 0x%x\n",
		res->start, res->end);
	dev_dbg(&i2c_imx->adapter.dev, "allocated %d bytes at 0x%x\n",
		resource_size(res), res->start);
	dev_dbg(&i2c_imx->adapter.dev, "adapter name: \"%s\"\n",
		i2c_imx->adapter.name);
	dev_info(&i2c_imx->adapter.dev, "IMX I2C adapter registered\n");

	return 0;   /* Return OK */
}

前面提到了,imx6自定义的数据结构 imx_i2c_struct ,里面包含了 i2c_adapter,截图记录。。
I2C总线(二)注册控制器_第4张图片

总结:对于每款芯片而言,每个i2c控制器都将会抽象成一个适配器(i2c_adapter)注册成i2c总线设备。

3、注册 i2c_adapter

调用 i2c_add_numbered_adapter 接口,注册 i2c_adapter,,,
移步 I2C总线(一)核心

你可能感兴趣的:(Linux内核和驱动,linux)