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