首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 操作系统 > UNIXLINUX >

linux上CPU注册i2c控制器(adapter)过程

2013-03-17 
linux下CPU注册i2c控制器(adapter)过程无论是三星的s3c2410, 还是cavium 的octeon, AMD的amd8111等等,任何

linux下CPU注册i2c控制器(adapter)过程

无论是三星的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 才能注册成功,当注册成功时会调用 platform_driver 结构元素 probe 函数指针,这里就是octeon_i2c_probe(), 当进入 probe 函数后,需要获取设备的资源信息,常用获取资源的函数主要是:

structresource * 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定义如下:


inti2c_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 inti2c_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,           };                                                      


staticstruct 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:


staticssize_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 :


staticstruct 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,                                                                                                             };                           

staticstruct attribute *i2c_dev_attrs[] = {
&dev_attr_name.attr,
/* modalias helpscoldplug: modprobe $(cat .../modalias) */
&dev_attr_modalias.attr,
NULL
};

两个属性结构体的定义在:

staticDEVICE_ATTR(name, S_IRUGO, show_name, NULL);

staticDEVICE_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/nameOCTEON 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,};      

热点排行
Bad Request.