无论是三星的s3c2410, 还是cavinum 的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 等等操作在任何处理器中的步骤大致都是相同的, 也都是不可或缺的. 内核代码drivers/i2c/busses/i2c-octeon.c /* i2c 控制器是被集成在CPU上的, 寄存器地址可以被CPU直接寻址到, linux将这个adapter抽象为一个platfrom device , 其驱动使用数据结构: struct platfrom_driver. 一般将usb host, serial 控制器等也做同样处理. 实现好driver后, 使用 platform_driver_register()函数将其注册到linux内核的设备树中. */ static int __init octeon_i2c_init(void) { int rv; 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 才能注册成功,当注册成功时会调用 platform_driver 结构元素 probe 函数指针,这里就是octeon_i2c_probe(), 当进入 probe 函数后,需要获取设备的资源信息,常用获取资源的函数主要是: struct resource * platform_get_resource(struct platform_device *dev, unsigned int type, unsigned int num); 根据参数 type 所指定类型,例如 IORESOURCE_MEM ,来获取指定的资源, 即获取设备的IO资源地址. */ res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (res_mem == NULL) { dev_err(i2c->dev, "found no memory resource\n"); result = -ENXIO; goto fail_region; } i2c->twsi_phys = res_mem->start; i2c->regsize = resource_size(res_mem); data = of_get_property(pdev->dev.of_node, "clock-rate", &len); if (data && len == sizeof(*data)) { /* 设置I2C时钟频率 */ i2c->twsi_freq = be32_to_cpup(data); } else { dev_err(i2c->dev, "no I2C 'clock-rate' property\n"); result = -ENXIO; goto fail_region; } /* 设置I2C系统IO时钟频率 */ i2c->sys_freq = octeon_get_io_clock_rate(); /* 申请IO域 */ if (!devm_request_mem_region(&pdev->dev, i2c->twsi_phys, i2c->regsize, res_mem->name)) { dev_err(i2c->dev, "request_mem_region failed\n"); goto fail_region; } /* 申请成功后将其映射到内核空间. */ i2c->twsi_base = ioremap(i2c->twsi_phys, i2c->regsize); /* 初始化I2C的等待队列 */ init_waitqueue_head(&i2c->queue); i2c->irq = irq; /* 注册I2C的中断号 */ result = request_irq(i2c->irq, octeon_i2c_isr, 0, DRV_NAME, i2c); if (result < 0) { dev_err(i2c->dev, "failed to attach interrupt\n"); goto fail_irq; } /* 初始化octeon I2C 控制器 */ result = octeon_i2c_initlowlevel(i2c); if (result) { dev_err(i2c->dev, "init low level failed\n"); goto fail_add; } /* 设置octeon I2C 时钟 */ result = octeon_i2c_setclock(i2c); /* 添加octeon I2C 的寄存器read/write实现方法 */ i2c->adap = octeon_i2c_ops; i2c->adap.timeout = msecs_to_jiffies(50); i2c->adap.dev.parent = &pdev->dev; i2c->adap.dev.of_node = pdev->dev.of_node; i2c_set_adapdata(&i2c->adap, i2c); platform_set_drvdata(pdev, i2c); /* 调用 i2c-core提供的注册adapter接口API. */ result = i2c_add_adapter(&i2c->adap); if (result < 0) { dev_err(i2c->dev, "failed to add adapter\n"); goto fail_add; } /* 注册adapter成功, 打印出当前版本号 */ dev_info(i2c->dev, "version %s\n", DRV_VERSION); /* of_i2c_register_devices最终调用的是i2c-core提供的i2c_new_device()函数, 建立一个的i2c adapter. */ of_i2c_register_devices(&i2c->adap); .. } 在此, octeon处理器先将定义好其特定的adapter数据结构, 将针对octeon处理器的 i2c 操作(i2c_algorithm)实现方法填充到此adapter结构体中, 最后, 使用 i2c-core提供的adapter注册函数 i2c_add_adapter(). 这样, 在用户层的一个i2c访问操作就会体现在octeon处理器层的i2c实现方法上. /* 注 : 关于: i2c_algorithm static struct i2c_adapter octeon_i2c_ops = { .owner = THIS_MODULE, .name = "OCTEON adapter", .algo = &octeon_i2c_algo, }; static const struct i2c_algorithm octeon_i2c_algo = { .master_xfer = octeon_i2c_xfer, .functionality = octeon_i2c_functionality, }; octeon 特定的 master_xfer 的实现就是octeon_i2c_xfer()函数, 在此有操作寄存器的方法. */ ********* ********* 下面介绍关于linux提供的i2c服务, linux 把一切deivce当作文件来处理, 其区别就是device 的属性和driver. I2C层提供的两个(可看作一个)重要的函数: i2c_add_adapter 和 i2c_add_numbered_adapter 这两个函数是i2c-core提供的注册i2c_adapter 的两个重要的接口函数, 本质是一样的, 最终都调用了i2c_register_adapter()函数. 两者的区别从命名上可以看出来: i2c_add_numbered_adapter() 增加了一个 numbered(有指定号码) 的 adapter. 可以理解为静态的总线号, 如果总线号被占用或非法等, 那么函数会返回相应的错误值. 而i2c_add_adapter ()函数是由系统自动分配总线号,即使用动态总线号, 如果注册成功, 得到的总线号保存在adapter->nr 成员中. CPU通过调用i2c_add_adapter 或 i2c_add_numbered_adapter来实现向内核文件系统注册自己的adapter. 比如上面的 octeon_i2c_probe()就使用了i2c_add_adapter, s3c2410最新版本使用的是i2c_add_numbered_adapter. I2c_add_adapter定义如下: int i2c_add_adapter(struct i2c_adapter *adapter) { int id, res = 0; retry: if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0) return -ENOMEM; mutex_lock(&core_lock); /* "above" here means "above or equal to", sigh */ res = idr_get_new_above(&i2c_adapter_idr, adapter, __i2c_first_dynamic_bus_num, &id); mutex_unlock(&core_lock); if (res < 0) { if (res == -EAGAIN) goto retry; return res; } adapter->nr = id; return i2c_register_adapter(adapter); } EXPORT_SYMBOL(i2c_add_adapter); static int i2c_register_adapter(struct i2c_adapter *adap) { ... mutex_init(&adap->bus_lock); /* Set default timeout to 1 second if not already set */ if (adap->timeout == 0) adap->timeout = HZ; dev_set_name(&adap->dev, "i2c-%d", adap->nr); adap->dev.bus = &i2c_bus_type; adap->dev.type = &i2c_adapter_type; /* 使用device_register注册该adapter设备 */ res = device_register(&adap->dev); .... } 在i2c_register_adapter中使用了两个重要的结构体, i2c_bus_type 和 i2c_adapter_type, linux将 adapter作为一个模拟为一个总线设备, 用 i2c_bus_type描述其总线类型, 用i2c_adapter_type描述其设备类型. struct device_type i2c_adapter_type = { .groups = i2c_adapter_attr_groups, .release = i2c_adapter_dev_release, }; EXPORT_SYMBOL_GPL(i2c_adapter_type); static const struct attribute_group *i2c_adapter_attr_groups[] = { &i2c_adapter_attr_group, NULL }; static struct attribute_group i2c_adapter_attr_group = { .attrs = i2c_adapter_attrs, }; static struct attribute *i2c_adapter_attrs[] = { &dev_attr_name.attr, &dev_attr_new_device.attr, &dev_attr_delete_device.attr, NULL }; 对于dev_attr_name, dev_attr_new_device, dev_attr_delete_device使用宏函数 DEVICE_ATTR, 将具体读写操作方法函数进行绑定, 请参考 DEVICE_ATTR宏的用法. I2c-core中: static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); static DEVICE_ATTR(new_device, S_IWUSR, NULL, i2c_sysfs_new_device); static DEVICE_ATTR(delete_device, S_IWUSR, NULL, i2c_sysfs_delete_device); 定义了show_name() 为结构体dev_attr_name 的show方法. 定义了i2c_sysfs_new_device() 为 结构体 dev_attr_new_device的store方法. 定义了i2c_sysfs_delete_device() 为 结构体dev_attr_delete_device的 store 方法. i2c_sysfs_new_device: static ssize_t i2c_sysfs_new_device(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { /* 获取adapter 数据结构 */ struct i2c_adapter *adap = to_i2c_adapter(dev); struct i2c_board_info info; struct i2c_client *client; .... client = i2c_new_device(adap, &info); ... } i2c_sysfs_new_device主要功能是: 获取adapter, 调用 i2c_new_device() struct i2c_client * i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info) { struct i2c_client *client; int status; ... client->adapter = adap; client->flags = info->flags; client->addr = info->addr; client->irq = info->irq; .. client->dev.parent = &client->adapter->dev; client->dev.bus = &i2c_bus_type; client->dev.type = &i2c_client_type; client->dev.of_node = info->of_node; status = device_register(&client->dev); .. } i2c_new_device() 主要功能是: 将adapter, irq, i2c_bus_type ,i2c_adapter_type等信息填充到一个 i2c_client中, 最后将i2c_client中的device注册到系统内核中. 上面使用到的另一个结构体 i2c_client_type : static struct device_type i2c_client_type; static struct device_type i2c_client_type = { .groups = i2c_dev_attr_groups, .uevent = i2c_device_uevent, .release = i2c_client_dev_release, }; static const struct attribute_group *i2c_dev_attr_groups[] = { &i2c_dev_attr_group, NULL }; static struct attribute_group i2c_dev_attr_group = { .attrs = i2c_dev_attrs, }; static struct attribute *i2c_dev_attrs[] = { &dev_attr_name.attr, /* modalias helps coldplug: modprobe $(cat .../modalias) */ &dev_attr_modalias.attr, NULL }; 两个属性结构体的定义在: static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); static DEVICE_ATTR(modalias, S_IRUGO, show_modalias, NULL); 这个属性的show_name定义为: static ssize_t show_name(struct device *dev, struct device_attribute *attr, char *buf) { return sprintf(buf, "%s\n", dev->type == &i2c_client_type ? to_i2c_client(dev)->name : to_i2c_adapter(dev)->name); } 举一个实际的例子: 在系统中执行: root@juson:~# cat /sys/bus/i2c/devices/i2c-0/name OCTEON adapter 显示: OCTEON adapter. 这个名字就是在 struct i2c_adapter octeon_i2c_ops 结构体中定义的: static struct i2c_adapter octeon_i2c_ops = { .owner = THIS_MODULE, .name = "OCTEON adapter", .algo = &octeon_i2c_algo, };