dm6467中I2C总线驱动分析
2012-04-23 16:06
381 查看
Linux下I2C驱动架构
如上图所示,每条I2C总线会对应一个adapter,而每条I2C总线上则可以有多个client,在linux kernel中,通过I2C core层将I2C client与I2C adapter关联起来,Linux 中I2C驱动代码位于drivers/i2c目录。 Linux中I2C可以分为三个层次,分别为I2C core层、I2C adapter driver层、I2C
device driver层。
I2C core层
I2C core是用于维护Linux的I2C核心部分,提供了核心的数据结构,I2C适配器驱动和设备驱动的注册、注销管理等API,同时还提供了I2C总线读写访问的一般接口(具体的实现在与I2C控制器相关的I2C adapter中实现)。 该层为硬件平台无关层,向下屏蔽了物理总线适配器的差异,定义了统一的访问策略和接口;向上则提供了统一的接口,以便I2C设备驱动可以通过总线适配器进行数据收发。 Linux中,I2C core层的代码位于driver/i2c/
i2c-core.c。由于该层是平台无关层,本文将不再叙述,有兴趣可以查阅相关资料。
I2C adapter driver层 I2C adapter driver层即I2C适配器驱动层,每种处理器平台都有自己的适配器驱动,属于平台移植相关层。它的职责是为系统中每条I2C总线实现相应的读写方法。但是适配器驱动本身并不会进行任何的通讯,而是等待设备驱动调用其函数。 在系统开机时,I2C适配器驱动被首先装载。一个适配器驱动用于支持一条特定的I2C总线的读写。一个适配器驱动通常需要两个模块,一个struct
i2c_adapter和一个struct i2c_algorithm来描述。
i2c adapter 构造一个对I2C core层接口的数据结构,并通过相应的接口函数向I2C core注册一个适配器。i2c_algorithm主要实现对I2C总线访问的算法,master_xfer和smbus_xfer即I2C adapter底层对I2C总线读写方法的实现,相关的数据结构如下:
主要就是master_xfer方法,其和具体的总线控制器相关,不同的CPU在实现上会有差异。
Algo是和底层硬件的接口,标识了具体的物理总线传输的实现。
Userspace_clients为使用该总线的client链表。
Nr为该适配器也就是某条I2C总线占据的全局编号。
bus_lock总线的互斥锁,防止总线冲突。
Linux中,I2C adapter driver层的代码位于drivers/i2c/busses目录。
I2C device driver层
I2C device driver层为用户接口层,其为用户提供了通过I2C总线访问具体设备的接口。
I2C的device driver层可以用两个模块来描述,struct i2c_driver和struct i2c_client。
i2c_client和i2c_driver分别构造对I2C core层接口的数据结构,并且通过相关的接口函数向 I2C Core注册I2C设备驱动。相关的数据结构如下:
Driver是为device服务的,i2c_driver注册时会扫描i2c bus上的设备,进行驱动和设备的绑定。主要有两种接口attach_adapter和probe,二者分别针对旧的和新式的驱动。
通常来说i2c_client对应着I2C总线上某个特定的slave或者是user space的某个用户对应,而此时的slave可以动态变化。
Linux中,I2C device driver层的代码位于drivers/i2c/chips目录。
dm6467 I2C adapter驱动
在Linux内核中,I2C adapter驱动位于drivers/i2c/busses目录下,DM6467 的I2C adapter驱动程序为drivers/i2c/busses/i2c-davinci.c
I2C adapter驱动,本质上就是实现了具体的总线传输算法并向核心层注册适配器。该驱动的注册采用Platform驱动和设备机制。
I2C adapter的Platform device
DM6467中Platform device的注册的代码位于内核的arch/arm/mach-davinci/devices.c
注册完成后,中断号及寄存器的基地址等信息会在设备树中描述了,此后只需利用 platform_get_resource等标准接口自动获取即可,实现了驱动和资源的分离。
I2C adapter的Platform driver
dm6467中Platform driver的注册的代码位于内核的ddrivers/i2c/busses/i2c-davinci.c中,该驱动的注册目的是初始化dm6467的I2C adapter,提供I2C总线传输的具体实现,并且向I2C core注册I2C adapter。
Platform driver的定义
在drivers/i2c/busses/i2c-davinci.c中Platform driver的定义 如下:
通过platform_driver_register()函数注册Platform driver davinci_i2c_driver时,会扫描platform bus上的所有设备,由于匹配因子是name即"i2c_davinci",而之前已经将name为"i2c_davinci"的Platform device注册到platform bus上,因此匹配成功,调用函数davinci_i2c_probe将设备和驱动绑定起来。
davinci_i2c_probe的过程如下:
通过platform_get_resource获*mem, *irq, 资源,分配内存,初始化struct davinci_i2c_dev,申请i2c_davinci_isr中断,初始化 struct i2c_adapter,并通过i2c-core提供的i2c_add_adapter注册i2c_adapter。具体如下:
这里定义了I2C adapter的中断处理函数i2c_davinci_isr(),该函数对I2C控制器的中断事件进行响应,主要实现了对I2C数据收发中断事件的处理。
结构i2c_davinci_algo 是adapter的I2C算法, i2c_davinci_xfer提供了I2C数据传输的实现。
482 static struct i2c_algorithm i2c_davinci_algo = {
483 .master_xfer = i2c_davinci_xfer,
484 .functionality = i2c_davinci_func,
485 };
在arch/arm/mach-davinci/i2c-client.c 注册了davinci_i2c_client_driver,并提供3个导出函数,提供给外面的模块调用。
64 EXPORT_SYMBOL(davinci_i2c_read);
89 EXPORT_SYMBOL(davinci_i2c_write);
161 EXPORT_SYMBOL(davinci_i2c_expander_op);
原文主要参考了:http://blog.csdn.net/kellycan/article/details/6394737
如上图所示,每条I2C总线会对应一个adapter,而每条I2C总线上则可以有多个client,在linux kernel中,通过I2C core层将I2C client与I2C adapter关联起来,Linux 中I2C驱动代码位于drivers/i2c目录。 Linux中I2C可以分为三个层次,分别为I2C core层、I2C adapter driver层、I2C
device driver层。
I2C core层
I2C core是用于维护Linux的I2C核心部分,提供了核心的数据结构,I2C适配器驱动和设备驱动的注册、注销管理等API,同时还提供了I2C总线读写访问的一般接口(具体的实现在与I2C控制器相关的I2C adapter中实现)。 该层为硬件平台无关层,向下屏蔽了物理总线适配器的差异,定义了统一的访问策略和接口;向上则提供了统一的接口,以便I2C设备驱动可以通过总线适配器进行数据收发。 Linux中,I2C core层的代码位于driver/i2c/
i2c-core.c。由于该层是平台无关层,本文将不再叙述,有兴趣可以查阅相关资料。
I2C adapter driver层 I2C adapter driver层即I2C适配器驱动层,每种处理器平台都有自己的适配器驱动,属于平台移植相关层。它的职责是为系统中每条I2C总线实现相应的读写方法。但是适配器驱动本身并不会进行任何的通讯,而是等待设备驱动调用其函数。 在系统开机时,I2C适配器驱动被首先装载。一个适配器驱动用于支持一条特定的I2C总线的读写。一个适配器驱动通常需要两个模块,一个struct
i2c_adapter和一个struct i2c_algorithm来描述。
i2c adapter 构造一个对I2C core层接口的数据结构,并通过相应的接口函数向I2C core注册一个适配器。i2c_algorithm主要实现对I2C总线访问的算法,master_xfer和smbus_xfer即I2C adapter底层对I2C总线读写方法的实现,相关的数据结构如下:
/* * The following structs are for those who like to implement new bus drivers: * i2c_algorithm is the interface to a class of hardware solutions which can * be addressed using the same bus algorithms - i.e. bit-banging or the PCF8584 * to name two of the most common. */ struct i2c_algorithm { /* If an adapter algorithm can't do I2C-level access, set master_xfer to NULL. If an adapter algorithm can do SMBus access, set smbus_xfer. If set to NULL, the SMBus protocol is simulated using common I2C messages */ /* master_xfer should return the number of messages successfully processed, or a negative value on error */ int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num); int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr, unsigned short flags, char read_write, u8 command, int size, union i2c_smbus_data *data); /* To determine what the adapter supports */ u32 (*functionality) (struct i2c_adapter *); };
主要就是master_xfer方法,其和具体的总线控制器相关,不同的CPU在实现上会有差异。
/* * i2c_adapter is the structure used to identify a physical i2c bus along * with the access algorithms necessary to access it. */ struct i2c_adapter { struct module *owner; unsigned int id; unsigned int class; /* classes to allow probing for */ const struct i2c_algorithm *algo; /* the algorithm to access the bus */ void *algo_data; /* data fields that are valid for all devices */ struct rt_mutex bus_lock; int timeout; /* in jiffies */ int retries; struct device dev; /* the adapter device */ int nr; char name[48]; struct completion dev_released; struct list_head userspace_clients; };
Algo是和底层硬件的接口,标识了具体的物理总线传输的实现。
Userspace_clients为使用该总线的client链表。
Nr为该适配器也就是某条I2C总线占据的全局编号。
bus_lock总线的互斥锁,防止总线冲突。
Linux中,I2C adapter driver层的代码位于drivers/i2c/busses目录。
I2C device driver层
I2C device driver层为用户接口层,其为用户提供了通过I2C总线访问具体设备的接口。
I2C的device driver层可以用两个模块来描述,struct i2c_driver和struct i2c_client。
i2c_client和i2c_driver分别构造对I2C core层接口的数据结构,并且通过相关的接口函数向 I2C Core注册I2C设备驱动。相关的数据结构如下:
/** * struct i2c_driver - represent an I2C device driver * @class: What kind of i2c device we instantiate (for detect) * @attach_adapter: Callback for bus addition (for legacy drivers) * @detach_adapter: Callback for bus removal (for legacy drivers) * @probe: Callback for device binding * @remove: Callback for device unbinding * @shutdown: Callback for device shutdown * @suspend: Callback for device suspend * @resume: Callback for device resume * @command: Callback for bus-wide signaling (optional) * @driver: Device driver model driver * @id_table: List of I2C devices supported by this driver * @detect: Callback for device detection * @address_list: The I2C addresses to probe (for detect) * @clients: List of detected clients we created (for i2c-core use only) * * The driver.owner field should be set to the module owner of this driver. * The driver.name field should be set to the name of this driver. * * For automatic device detection, both @detect and @address_data must * be defined. @class should also be set, otherwise only devices forced * with module parameters will be created. The detect function must * fill at least the name field of the i2c_board_info structure it is * handed upon successful detection, and possibly also the flags field. * * If @detect is missing, the driver will still work fine for enumerated * devices. Detected devices simply won't be supported. This is expected * for the many I2C/SMBus devices which can't be detected reliably, and * the ones which can always be enumerated in practice. * * The i2c_client structure which is handed to the @detect callback is * not a real i2c_client. It is initialized just enough so that you can * call i2c_smbus_read_byte_data and friends on it. Don't do anything * else with it. In particular, calling dev_dbg and friends on it is * not allowed. */ struct i2c_driver { unsigned int class; /* Notifies the driver that a new bus has appeared or is about to be * removed. You should avoid using this if you can, it will probably * be removed in a near future. */ int (*attach_adapter)(struct i2c_adapter *); int (*detach_adapter)(struct i2c_adapter *); /* Standard driver model interfaces */ int (*probe)(struct i2c_client *, const struct i2c_device_id *); int (*remove)(struct i2c_client *); /* driver model interfaces that don't relate to enumeration */ void (*shutdown)(struct i2c_client *); int (*suspend)(struct i2c_client *, pm_message_t mesg); int (*resume)(struct i2c_client *); /* Alert callback, for example for the SMBus alert protocol. * The format and meaning of the data value depends on the protocol. * For the SMBus alert protocol, there is a single bit of data passed * as the alert response's low bit ("event flag"). */ void (*alert)(struct i2c_client *, unsigned int data); /* a ioctl like command that can be used to perform specific functions * with the device. */ int (*command)(struct i2c_client *client, unsigned int cmd, void *arg); struct device_driver driver; const struct i2c_device_id *id_table; /* Device detection callback for automatic device creation */ int (*detect)(struct i2c_client *, struct i2c_board_info *); const unsigned short *address_list; struct list_head clients; };
Driver是为device服务的,i2c_driver注册时会扫描i2c bus上的设备,进行驱动和设备的绑定。主要有两种接口attach_adapter和probe,二者分别针对旧的和新式的驱动。
通常来说i2c_client对应着I2C总线上某个特定的slave或者是user space的某个用户对应,而此时的slave可以动态变化。
Linux中,I2C device driver层的代码位于drivers/i2c/chips目录。
dm6467 I2C adapter驱动
在Linux内核中,I2C adapter驱动位于drivers/i2c/busses目录下,DM6467 的I2C adapter驱动程序为drivers/i2c/busses/i2c-davinci.c
I2C adapter驱动,本质上就是实现了具体的总线传输算法并向核心层注册适配器。该驱动的注册采用Platform驱动和设备机制。
I2C adapter的Platform device
DM6467中Platform device的注册的代码位于内核的arch/arm/mach-davinci/devices.c
在arch/arm/mach-davinci/devices.c中。Platmform device 定义如下: 32 static struct resource i2c_resources[] = { 33 { 34 .start = D***INCI_I2C_BASE, 35 .end = D***INCI_I2C_BASE + 0x40, 36 .flags = IORESOURCE_MEM, 37 }, 38 { 39 .start = IRQ_I2C, 40 .flags = IORESOURCE_IRQ, 41 }, 42 }; 43 44 static struct davinci_i2c_platform_data dm644x_i2c_data = { 45 .bus_freq = 20, 46 .bus_delay = 100, 47 }; 48 49 static struct davinci_i2c_platform_data dm355_i2c_data = { 50 .bus_freq = 20, 51 .bus_delay = 100, 52 }; 53 54 static struct davinci_i2c_platform_data dm646x_i2c_data = { 55 .bus_freq = 100, 56 .bus_delay = 0, 57 }; 58 59 static struct platform_device i2c_device = { 60 .name = "i2c_davinci", 61 .id = 1, 62 .dev = { 63 .platform_data = &dm355_i2c_data, 64 }, 65 .num_resources = ARRAY_SIZE(i2c_resources), 66 .resource = i2c_resources, 67 }; i2c_resources[] 定义了I2C的地址范围、中断号等相关信息, platform_device i2c_device 初始化的时候初始化了dm355的数据,在davinci_init_cpu_i2c中再给i2c_device.dev.platform_data赋值为dm6467的数据。 132 static struct platform_device *devices[] __initdata = { 133 &i2c_device, 134 &watchdog_device, 135 &usb_device, 136 }; 138 static void __init davinci_init_cpu_i2c(void) 139 { 140 if (cpu_is_davinci_dm644x()) 141 i2c_device.dev.platform_data = &dm644x_i2c_data; 142 else if (cpu_is_davinci_dm6467()) 143 i2c_device.dev.platform_data = &dm646x_i2c_data; 144 145 /* all others default to use dm355 because dm355 uses the max speed */ 146 } 147 最后在davinci_init_devices中调用drivers/base/platform.c 提供的导出函数platform_add_devices添加设备。 160 static int __init davinci_init_devices(void) 161 { 162 davinci_init_cpu_i2c(); 163 davinci_init_cpu_usb(); 164 platform_add_devices(devices, ARRAY_SIZE(devices)); 165 return 0; 166 } 167 arch_initcall(davinci_init_devices); platform_add_devices函数具体如下: 110 int platform_add_devices(struct platform_device **devs, int num) 111 { 112 int i, ret = 0; 113 114 for (i = 0; i < num; i++) { 115 ret = platform_device_register(devs[i]); 116 if (ret) { 117 while (--i >= 0) 118 platform_device_unregister(devs[i]); 119 break; 120 } 121 } 122 123 return ret; 124 } 125 EXPORT_SYMBOL_GPL(platform_add_devices);
注册完成后,中断号及寄存器的基地址等信息会在设备树中描述了,此后只需利用 platform_get_resource等标准接口自动获取即可,实现了驱动和资源的分离。
platform_get_resource如下: 30 /** 31 * platform_get_resource - get a resource for a device 32 * @dev: platform device 33 * @type: resource type 34 * @num: resource index 35 */ 36 struct resource * 37 platform_get_resource(struct platform_device *dev, unsigned int type, 38 unsigned int num) 39 { 40 int i; 41 42 for (i = 0; i < dev->num_resources; i++) { 43 struct resource *r = &dev->resource[i]; 44 45 if ((r->flags & (IORESOURCE_IO|IORESOURCE_MEM| 46 IORESOURCE_IRQ|IORESOURCE_DMA)) 47 == type) 48 if (num-- == 0) 49 return r; 50 } 51 return NULL; 52 }
I2C adapter的Platform driver
dm6467中Platform driver的注册的代码位于内核的ddrivers/i2c/busses/i2c-davinci.c中,该驱动的注册目的是初始化dm6467的I2C adapter,提供I2C总线传输的具体实现,并且向I2C core注册I2C adapter。
Platform driver的定义
在drivers/i2c/busses/i2c-davinci.c中Platform driver的定义 如下:
599 static struct platform_driver davinci_i2c_driver = { 600 .probe = davinci_i2c_probe, 601 .remove = davinci_i2c_remove, 602 .driver = { 603 .name = "i2c_davinci", 604 .owner = THIS_MODULE, 605 }, 606 }; 其注册方法如下: 608 /* I2C may be needed to bring up other drivers */ 609 static int __init davinci_i2c_init_driver(void) 610 { 611 return platform_driver_register(&davinci_i2c_driver); 612 } 613 subsys_initcall(davinci_i2c_init_driver);
通过platform_driver_register()函数注册Platform driver davinci_i2c_driver时,会扫描platform bus上的所有设备,由于匹配因子是name即"i2c_davinci",而之前已经将name为"i2c_davinci"的Platform device注册到platform bus上,因此匹配成功,调用函数davinci_i2c_probe将设备和驱动绑定起来。
davinci_i2c_probe的过程如下:
通过platform_get_resource获*mem, *irq, 资源,分配内存,初始化struct davinci_i2c_dev,申请i2c_davinci_isr中断,初始化 struct i2c_adapter,并通过i2c-core提供的i2c_add_adapter注册i2c_adapter。具体如下:
487 static int davinci_i2c_probe(struct platform_device *pdev) 488 { 489 struct davinci_i2c_dev *dev; 490 struct i2c_adapter *adap; 491 struct resource *mem, *irq, *ioarea; 492 int r; 493 494 /* NOTE: driver uses the static register mapping */ 495 mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 496 if (!mem) { 497 dev_err(&pdev->dev, "no mem resource?\n"); 498 return -ENODEV; 499 } 500 501 irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); 502 if (!irq) { 503 dev_err(&pdev->dev, "no irq resource?\n"); 504 return -ENODEV; 505 } 506 507 ioarea = request_mem_region(mem->start, (mem->end - mem->start) + 1, 508 pdev->name); 509 if (!ioarea) { 510 dev_err(&pdev->dev, "I2C region already claimed\n"); 511 return -EBUSY; 512 } 513 514 dev = kzalloc(sizeof(struct davinci_i2c_dev), GFP_KERNEL); 515 if (!dev) { 516 r = -ENOMEM; 517 goto err_release_region; 518 } 519 520 dev->dev = get_device(&pdev->dev); 521 dev->irq = irq->start; 522 platform_set_drvdata(pdev, dev); 523 524 dev->clk = clk_get(&pdev->dev, "I2CCLK"); 525 if (IS_ERR(dev->clk)) { 526 r = -ENODEV; 527 goto err_free_mem; 528 } 529 clk_enable(dev->clk); 530 531 dev->base = (void __iomem *)IO_ADDRESS(mem->start); 532 i2c_davinci_init(dev); 533 534 r = request_irq(dev->irq, i2c_davinci_isr, 0, pdev->name, dev); 535 if (r) { 536 dev_err(&pdev->dev, "failure requesting irq %i\n", dev->irq); 537 goto err_unuse_clocks; 538 } 539 540 adap = &dev->adapter; 541 i2c_set_adapdata(adap, dev); 542 adap->owner = THIS_MODULE; 543 adap->class = I2C_CLASS_HWMON; 544 strlcpy(adap->name, "DaVinci I2C adapter", sizeof(adap->name)); 545 adap->algo = &i2c_davinci_algo; 546 adap->dev.parent = &pdev->dev; 547 548 /* FIXME */ 549 adap->timeout = 1; 550 adap->retries = 1; 551 552 adap->nr = pdev->id; 553 r = i2c_add_adapter(adap); 554 if (r) { 555 dev_err(&pdev->dev, "failure adding adapter\n"); 556 goto err_free_irq; 557 } 558 559 return 0; 560 561 err_free_irq: 562 free_irq(dev->irq, dev); 563 err_unuse_clocks: 564 clk_disable(dev->clk); 565 clk_put(dev->clk); 566 dev->clk = NULL; 567 err_free_mem: 568 platform_set_drvdata(pdev, NULL); 569 put_device(&pdev->dev); 570 kfree(dev); 571 err_release_region: 572 release_mem_region(mem->start, (mem->end - mem->start) + 1); 573 574 return r; 575 }
这里定义了I2C adapter的中断处理函数i2c_davinci_isr(),该函数对I2C控制器的中断事件进行响应,主要实现了对I2C数据收发中断事件的处理。
结构i2c_davinci_algo 是adapter的I2C算法, i2c_davinci_xfer提供了I2C数据传输的实现。
482 static struct i2c_algorithm i2c_davinci_algo = {
483 .master_xfer = i2c_davinci_xfer,
484 .functionality = i2c_davinci_func,
485 };
在arch/arm/mach-davinci/i2c-client.c 注册了davinci_i2c_client_driver,并提供3个导出函数,提供给外面的模块调用。
64 EXPORT_SYMBOL(davinci_i2c_read);
89 EXPORT_SYMBOL(davinci_i2c_write);
161 EXPORT_SYMBOL(davinci_i2c_expander_op);
原文主要参考了:http://blog.csdn.net/kellycan/article/details/6394737
相关文章推荐
- DM6467中I2C总线驱动分析
- OMAP3630 Linux I2C总线驱动分析
- linux下I2C总线驱动架构分析
- OMAP3630 Linux I2C总线驱动分析(1)
- OMAP3630 Linux I2C总线驱动分析(2)
- i2c适配器驱动源码分析(i2c总线驱动)
- Linux I2C驱动分析(一)----I2C架构和总线驱动
- Linux I2C驱动分析(一)----I2C架构和总线驱动
- Linux I2C驱动分析(一)----I2C架构和总线驱动
- Linux I2C驱动分析(一)----I2C架构和总线驱动
- OMAP3630 Linux I2C总线驱动分析
- Linux设备驱动之I2C总线适配器驱动分析
- Linux I2C子系统分析-I2C总线驱动 1
- Linux设备驱动---OMAP3630 Linux I2C总线驱动分析(1)
- Linux I2C子系统分析-I2C总线驱动
- Linux I2C子系统分析-I2C总线驱动
- Linux I2C驱动分析(一)----I2C架构和总线驱动
- 学习笔记 --- LINUX I2C总线驱动框架分析
- linux-2.6.22.6 i2c总线驱动分析
- Linux I2C子系统分析-I2C总线驱动&&Linux I2C子系统分析-I2C设备驱动