无论是三星的s3c2410, 还是cavium 的octeon, AMD的amd8111等等, 任何处理器在linux下添加自己的adapter都是大致的方法, 都是实现自己的driver, 最后调用i2c-core提供的API完成整个注册. 广泛地讲, linux将任何类型的设备, 任何类型的总线等都作为文件来处理, 只不过使用了不同的数据结构的driver和device. I2c的逻辑简单实用. 在linux精妙的架构下, 代码量非常小. 现在大部分的IC都有I2C接口. 至于spi, uart, can, usb, pci, stat等等各种各样的, 虽然协议不同, 特点不用, 但本质上都是一样的. 至于I2C具体的协议, 时序等请参考其他资料. 这里只做软件上的架构分析. 下面以octeon处理器为例, 重点介绍下octeon_i2c_probe()中部分重要的代码, 在其他处理器的xxx_i2c_probe() 函数中, 无论是获取设备资源, 获取中断, CPU时钟, 配置adapter 等等操作在任何处理器中的步骤大致都是相同的, 也都是不可或缺的. By 韩大卫@吉林师范大学
内核代码 drivers/i2c/busses/i2c-octeon.c
static int __init octeon_i2c_init(void) { int rv;
/* i2c 控制器是被集成在CPU上的, 寄存器地址可以被CPU直接寻址到, linux将这个adapter抽象为一个platfrom device , 其驱动使用数据结构: struct platfrom_driver. 一般将usb host, serial 控制器等也做同样处理. 实现好driver后, 使用 platform_driver_register()函数将其注册到linux内核的设备树中. */
rv = platform_driver_register(&octeon_i2c_driver); return rv; } static struct platform_driver octeon_i2c_driver = { .probe = octeon_i2c_probe, .remove = __devexit_p(octeon_i2c_remove), .driver = { .owner = THIS_MODULE, .name = DRV_NAME, .of_match_table = octeon_i2c_match, }, }; #define DRV_NAME "i2c-octeon" static int __devinit octeon_i2c_probe(struct platform_device *pdev) { int irq, result = 0; struct octeon_i2c *i2c; struct resource *res_mem; const __be32 *data; int len; /* 获取设备的中断号 */ /* All adaptors have an irq. */ irq = platform_get_irq(pdev, 0); /* 为其数据结构分配内存 */ i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL); if (!i2c) { dev_err(&pdev->dev, "kzalloc failed\n"); result = -ENOMEM; goto out; } i2c->dev = &pdev->dev; /* platform_driver_register() 注册时会对所有已注册的所有 platform_device 中的 name 和当前注册的 platform_driver 的driver.name 进行比较,只有找到相同的名称的 platfomr_device