一、i2c适配器
i2c适配器在硬件层面其实就是i2c控制器,因为跟芯片相关,一般内核会带对应厂商的芯片驱动,实现在i2c/busses中找好了。
我们直接看代码,以imx6为例。
1、平台总线匹配
imx6中是做了,驱动和设备树分离,以平台总线实现,
我们主要关注点是:
1、设备树中,找到有设备,节点为i2c1,
2、平台驱动中有定义驱动,并且我们看到 compatible 可以匹配上,
那么将,执行 i2c_imx_probe 。。。
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;elsei2c_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控制器都将会抽象成一个适配器(i2c_adapter)注册成i2c总线设备。
3、注册 i2c_adapter
调用 i2c_add_numbered_adapter 接口,注册 i2c_adapter,,,
移步 I2C总线(一)核心