dm6467中I2C总线驱动分析

2023-10-18 23:59
文章标签 分析 驱动 总线 i2c dm6467

本文主要是介绍dm6467中I2C总线驱动分析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

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_xfersmbus_xferI2C 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_xferto NULL. If an adapter algorithm can do SMBus access, setsmbus_xfer. If set to NULL, the SMBus protocol is simulatedusing common I2C messages *//* master_xfer should return the number of messages successfullyprocessed, 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总线访问具体设备的接口。
    I2Cdevice driver层可以用两个模块来描述,struct i2c_driverstruct i2c_client
    i2c_clienti2c_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_adapterprobe,二者分别针对旧的和新式的驱动。 

通常来说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 adapterPlatform device
   DM6467Platform device的注册的代码位于内核的arch/arm/mach-davinci/devices.c

	在arch/arm/mach-davinci/devices.c中。Platmform device 定义如下:32 static struct resource i2c_resources[] = {33         {34                 .start          = DAVINCI_I2C_BASE,35                 .end            = DAVINCI_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 device32  *      @dev: platform device33  *      @type: resource type34  *      @num: resource index35  */                     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 adapterPlatform driver
dm6467Platform driver的注册的代码位于内核的ddrivers/i2c/busses/i2c-davinci.c中,该驱动的注册目的是初始化dm6467I2C 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 是adapterI2C算法, 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总线驱动分析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/235953

相关文章

MyBatis Plus 中 update_time 字段自动填充失效的原因分析及解决方案(最新整理)

《MyBatisPlus中update_time字段自动填充失效的原因分析及解决方案(最新整理)》在使用MyBatisPlus时,通常我们会在数据库表中设置create_time和update... 目录前言一、问题现象二、原因分析三、总结:常见原因与解决方法对照表四、推荐写法前言在使用 MyBATis

Python主动抛出异常的各种用法和场景分析

《Python主动抛出异常的各种用法和场景分析》在Python中,我们不仅可以捕获和处理异常,还可以主动抛出异常,也就是以类的方式自定义错误的类型和提示信息,这在编程中非常有用,下面我将详细解释主动抛... 目录一、为什么要主动抛出异常?二、基本语法:raise关键字基本示例三、raise的多种用法1. 抛

github打不开的问题分析及解决

《github打不开的问题分析及解决》:本文主要介绍github打不开的问题分析及解决,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、找到github.com域名解析的ip地址二、找到github.global.ssl.fastly.net网址解析的ip地址三

Mysql的主从同步/复制的原理分析

《Mysql的主从同步/复制的原理分析》:本文主要介绍Mysql的主从同步/复制的原理分析,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录为什么要主从同步?mysql主从同步架构有哪些?Mysql主从复制的原理/整体流程级联复制架构为什么好?Mysql主从复制注意

java -jar命令运行 jar包时运行外部依赖jar包的场景分析

《java-jar命令运行jar包时运行外部依赖jar包的场景分析》:本文主要介绍java-jar命令运行jar包时运行外部依赖jar包的场景分析,本文给大家介绍的非常详细,对大家的学习或工作... 目录Java -jar命令运行 jar包时如何运行外部依赖jar包场景:解决:方法一、启动参数添加: -Xb

Apache 高级配置实战之从连接保持到日志分析的完整指南

《Apache高级配置实战之从连接保持到日志分析的完整指南》本文带你从连接保持优化开始,一路走到访问控制和日志管理,最后用AWStats来分析网站数据,对Apache配置日志分析相关知识感兴趣的朋友... 目录Apache 高级配置实战:从连接保持到日志分析的完整指南前言 一、Apache 连接保持 - 性

Linux中的more 和 less区别对比分析

《Linux中的more和less区别对比分析》在Linux/Unix系统中,more和less都是用于分页查看文本文件的命令,但less是more的增强版,功能更强大,:本文主要介绍Linu... 目录1. 基础功能对比2. 常用操作对比less 的操作3. 实际使用示例4. 为什么推荐 less?5.

spring-gateway filters添加自定义过滤器实现流程分析(可插拔)

《spring-gatewayfilters添加自定义过滤器实现流程分析(可插拔)》:本文主要介绍spring-gatewayfilters添加自定义过滤器实现流程分析(可插拔),本文通过实例图... 目录需求背景需求拆解设计流程及作用域逻辑处理代码逻辑需求背景公司要求,通过公司网络代理访问的请求需要做请

Java集成Onlyoffice的示例代码及场景分析

《Java集成Onlyoffice的示例代码及场景分析》:本文主要介绍Java集成Onlyoffice的示例代码及场景分析,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要... 需求场景:实现文档的在线编辑,团队协作总结:两个接口 + 前端页面 + 配置项接口1:一个接口,将o

IDEA下"File is read-only"可能原因分析及"找不到或无法加载主类"的问题

《IDEA下Fileisread-only可能原因分析及找不到或无法加载主类的问题》:本文主要介绍IDEA下Fileisread-only可能原因分析及找不到或无法加载主类的问题,具有很好的参... 目录1.File is read-only”可能原因2.“找不到或无法加载主类”问题的解决总结1.File