I2C子系统详解3——I2C总线驱动层代码分析

以下内容源于朱有鹏嵌入式课程的学习与整理,如有侵权请告知删除。

一、前言

由I2C总线设备的驱动框架可知,I2C总线设备的驱动框架涉及的文件如下:

(1)I2C设备驱动层相关的文件
x210开发板的电容触摸屏gslX680采用I2C接口,因此以这个I2C设备为例进行分析。

其对应的驱动源代码文件是x210_kernel\drivers\input\touchscreen\gslX680.c文件。

它由触摸屏IC原厂工程师提供,代码中涉及到很多触摸屏专业方面的知识,但无需理会。

gslX680.c文件进行了I2C设备驱动的注册,相应的I2C设备注册是在mach-x210.c文件中。

(2)I2C核心层相关的文件
I2C核心层相关的文件是i2c-core.c文件,它由内核开发者提供,和具体的硬件操作无关。

(3)I2C总线驱动层相关的文件
algos目录:存放的是I2C通信的算法(主要是时序等内容)。

busses目录:存放的是各种已经编写好的、将来要向i2c核心层注册的适配器文件。

我们主要分析drivers\i2c\busses\i2c-s3c2410.c文件。


下面将对I2C总线驱动层的文件进行分析,主要分析drivers\i2c\busses\i2c-s3c2410.c

 

二、I2C总线驱动层代码分析

I2C总线驱动是I2C适配器(I2C控制器)的软件实现,提供I2C适配器与从设备的数据通信功能。

I2C总线驱动由 I2C适配器模块 和 I2C通信算法 来描述。

1、I2C适配器模块的注册

从下面代码(位于i2c-s3c2410.c文件中)可知,adapter的注册实现为模块的方式,因此可以进行动态的加载和卸载,并且是基于platform平台总线的(因为I2C控制器属于内部外设。)。i2c-s3c2410.c文件提供的是平台驱动的注册,平台设备的注册一般在板级文件即mach文件中实现(比如mach-x210.c文件)。

static struct platform_driver s3c24xx_i2c_driver = {.probe		= s3c24xx_i2c_probe, //平台总线驱动的probe函数.remove		= s3c24xx_i2c_remove,.id_table	= s3c24xx_driver_ids,//平台总线驱动所支持的设备列表.driver		= {.owner	= THIS_MODULE,.name	= "s3c-i2c", //平台总线驱动的名字,与平台总线设备的名字相同.pm	= S3C24XX_DEV_PM_OPS,},
};
static int __init i2c_adap_s3c_init(void)
{return platform_driver_register(&s3c24xx_i2c_driver);
}
subsys_initcall(i2c_adap_s3c_init);

2、s3c24xx_i2c_probe函数分析

该函数内容如下,主要的内容是

(1)填充一个i2c_adapter结构体,并且调用接口去注册之。

(2)从platform_device接收硬件信息,做必要的处理(request_mem_region & ioremap、request_irq等)。

(3)对硬件做初始化(直接操作210内部I2C控制器的寄存器)。

static int s3c24xx_i2c_probe(struct platform_device *pdev)
{struct s3c24xx_i2c *i2c;               //   次结构体是三星对本SoC中的i2c控制器的一个描述,是一个用来在多文件中进行数据传递的全局结构体struct s3c2410_platform_i2c *pdata;    //   此结构体用来表示平台设备的私有数据struct resource *res;int ret;pdata = pdev->dev.platform_data;       //  获取到平台设备层中的平台数据if (!pdata) {dev_err(&pdev->dev, "no platform data\n");return -EINVAL;}i2c = kzalloc(sizeof(struct s3c24xx_i2c), GFP_KERNEL); //  给s3c24xx_i2c类型指针申请分配内存空间if (!i2c) {dev_err(&pdev->dev, "no memory for state\n");return -ENOMEM;}//   以下主要是对s3c24xx_i2c 结构体中的i2c_adapter变量的一个填充strlcpy(i2c->adap.name, "s3c2410-i2c", sizeof(i2c->adap.name)); //  设置适配器的名字    s3c2410-i2ci2c->adap.owner   = THIS_MODULE;i2c->adap.algo    = &s3c24xx_i2c_algorithm;                     //  通信算法i2c->adap.retries = 2;i2c->adap.class   = I2C_CLASS_HWMON | I2C_CLASS_SPD;            //  该适配器所支持的次设备类有哪些i2c->tx_setup     = 50;spin_lock_init(&i2c->lock);       //  初始化互斥锁init_waitqueue_head(&i2c->wait);  //  初始化工作队列/* find the clock and enable it */i2c->dev = &pdev->dev;            //  通过s3c24xx_i2c->dev 指针指向平台设备的device结构体i2c->clk = clk_get(&pdev->dev, "i2c");if (IS_ERR(i2c->clk)) {dev_err(&pdev->dev, "cannot get clock\n");ret = -ENOENT;goto err_noclk;}dev_dbg(&pdev->dev, "clock source %p\n", i2c->clk);clk_enable(i2c->clk);             //  使能时钟/* map the registers */res = platform_get_resource(pdev, IORESOURCE_MEM, 0);  //  获取平台设备资源if (res == NULL) {dev_err(&pdev->dev, "cannot find IO resource\n");ret = -ENOENT;goto err_clk;}i2c->ioarea = request_mem_region(res->start, resource_size(res), //  物理地址到虚拟地址的映射请求pdev->name);if (i2c->ioarea == NULL) {dev_err(&pdev->dev, "cannot request IO\n");ret = -ENXIO;goto err_clk;}i2c->regs = ioremap(res->start, resource_size(res));    //  地址映射if (i2c->regs == NULL) {dev_err(&pdev->dev, "cannot map IO\n");ret = -ENXIO;goto err_ioarea;}dev_dbg(&pdev->dev, "registers %p (%p, %p)\n",i2c->regs, i2c->ioarea, res);/* setup info block for the i2c core */i2c->adap.algo_data = i2c;          //  将s3c24xx_i2c 结构体变量作为s3c24xx_i2c中内置的i2c_adapter适配器中的私有数据i2c->adap.dev.parent = &pdev->dev;  //  指定适配器设备的父设备是平台设备device :   /sys/devices/platform/s3c2410-i2cn这个目录下/* initialise the i2c controller */ret = s3c24xx_i2c_init(i2c);        //  i2c控制器(适配器)    寄存器相关的配置if (ret != 0)goto err_iomap;/* find the IRQ for this unit (note, this relies on the init call to* ensure no current IRQs pending*/i2c->irq = ret = platform_get_irq(pdev, 0); //  获取平台设备中的i2c中断号(这个中断是I2C控制器产生的中断)if (ret <= 0) {dev_err(&pdev->dev, "cannot find IRQ\n");goto err_iomap;}ret = request_irq(i2c->irq, s3c24xx_i2c_irq, IRQF_DISABLED,  //  申请中断dev_name(&pdev->dev), i2c);if (ret != 0) {dev_err(&pdev->dev, "cannot claim IRQ %d\n", i2c->irq);goto err_iomap;}ret = s3c24xx_i2c_register_cpufreq(i2c);   //  这个不清楚if (ret < 0) {dev_err(&pdev->dev, "failed to register cpufreq notifier\n");goto err_irq;}/* Note, previous versions of the driver used i2c_add_adapter()* to add the bus at any number. We now pass the bus number via* the platform data, so if unset it will now default to always* being bus 0.*/i2c->adap.nr = pdata->bus_num;              //  确定i2c主机(适配器)的编号ret = i2c_add_numbered_adapter(&i2c->adap); //  向i2c核心注册i2c适配器  /sys/devices/platform/s3c2410-i2cn/s3c2410-i2c   因为在函数内会将 i2c-%d作为适配器的名字if (ret < 0) {dev_err(&pdev->dev, "failed to add bus to i2c core\n");goto err_cpufreq;}platform_set_drvdata(pdev, i2c);   //  将s3c24xx_i2c变量作为平台设备私有数据中的设备驱动私有数据  dev->p->driver_data//  因为这个变量还会在本文件中其他函数中会用到了clk_disable(i2c->clk);dev_info(&pdev->dev, "%s: S3C I2C adapter\n", dev_name(&i2c->adap.dev));return 0;err_cpufreq:s3c24xx_i2c_deregister_cpufreq(i2c);err_irq:free_irq(i2c->irq, i2c);err_iomap:iounmap(i2c->regs);err_ioarea:release_resource(i2c->ioarea);kfree(i2c->ioarea);err_clk:clk_disable(i2c->clk);clk_put(i2c->clk);err_noclk:kfree(i2c);return ret;
}

3、struct i2c_algorithm结构体

上面的probe函数中有一句代码:

i2c->adap.algo  = &s3c24xx_i2c_algorithm;

其中变量s3c24xx_i2c_algorithm的定义如下,可见它是struct i2c_algorithm类型变量。

static const struct i2c_algorithm s3c24xx_i2c_algorithm = {.master_xfer		= s3c24xx_i2c_xfer,.functionality		= s3c24xx_i2c_func,
};

(1)s3c24xx_i2c_xfer函数

s3c24xx_i2c_xfer函数内部调用了s3c24xx_i2c_doxfer函数,后者是信息传输函数。

/* s3c24xx_i2c_xfer** first port of call from the i2c bus code when an message needs* transferring across the i2c bus.
*/static int s3c24xx_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg *msgs, int num)
{struct s3c24xx_i2c *i2c = (struct s3c24xx_i2c *)adap->algo_data;int retry;int ret;clk_enable(i2c->clk);for (retry = 0; retry < adap->retries; retry++) {ret = s3c24xx_i2c_doxfer(i2c, msgs, num);if (ret != -EAGAIN)goto out;dev_dbg(i2c->dev, "Retrying transmission (%d)\n", retry);udelay(200);}ret = -EREMOTEIO;
out:clk_disable(i2c->clk);return ret;
}
/* s3c24xx_i2c_doxfer** this starts an i2c transfer
*/static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c,struct i2c_msg *msgs, int num)
{unsigned long timeout;int ret;if (i2c->suspended)return -EIO;ret = s3c24xx_i2c_set_master(i2c);//设置成主机模式if (ret != 0) {dev_err(i2c->dev, "cannot get bus (error %d)\n", ret);ret = -EAGAIN;goto out;}spin_lock_irq(&i2c->lock);i2c->msg     = msgs; //填充i2c->msg_num = num;i2c->msg_ptr = 0;i2c->msg_idx = 0;i2c->state   = STATE_START;s3c24xx_i2c_enable_irq(i2c);s3c24xx_i2c_message_start(i2c, msgs);spin_unlock_irq(&i2c->lock);timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5);ret = i2c->msg_idx;/* having these next two as dev_err() makes life very* noisy when doing an i2cdetect */if (timeout == 0)dev_dbg(i2c->dev, "timeout\n");else if (ret != num)dev_dbg(i2c->dev, "incomplete xfer (%d)\n", ret);/* ensure the stop has been through the bus */udelay(10);out:return ret;
}

注意其中的struct i2c_msg结构体,其表示一个通信周期的数据相关的结构体,包括从设备的信息,通信数据长度等等。其内容如下:

struct i2c_msg {__u16 addr;           /* slave address 设备地址 */__u16 flags;          /* 本次消息的标志位,就是下面的这些 */
#define I2C_M_TEN           0x0010    /* 设置了这个标志位表示从设备的地址是10bit */
#define I2C_M_RD            0x0001    /* 设置了这个标志位表示本次通信i2c控制器是处于接收方,否则就是发送方 */
#define I2C_M_NOSTART       0x4000
#define I2C_M_REV_DIR_ADDR  0x2000    /* 设置这个标志位表示需要将读写标志位反转过来 */
#define I2C_M_IGNORE_NAK    0x1000    /* 设置这个标志意味当前i2c_msg忽略I2C器件的ack和nack信号 */
#define I2C_M_NO_RD_ACK     0x0800    /* 设置这个标志位表示在读操作中主机不用ACK */
#define I2C_M_RECV_LEN      0x0400__u16 len;                        /* 数据长度 */__u8 *buf;                        /* 数据缓冲区指针 */
};

在s3c24xx_i2c_doxfer函数中填充的通信算法会在i2c主设备与从设备通信的时候调用。

(2)s3c24xx_i2c_func函数

该函数表明I2C接口支持哪些特性,内容如下:

/* declare our i2c functionality */
static u32 s3c24xx_i2c_func(struct i2c_adapter *adap)
{return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_PROTOCOL_MANGLING;
}


 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/461131.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java join 异常_Java:守护进程:thread.join()没有完成,当在一个线程中抛出异常时...

我写了一个Java守护进程(一个实现守护进程和Runnable的类)&#xff0c;现在我遇到了以下问题&#xff1a;在init()中&#xff0c;我创建了一个新线程 . Thread thread new Thread(this); 在start()中我启动新线程 . thread.start() . 在运行中我做了很多不同的事情......然后发…

硬链接与符号链接的比较?

今天就说说硬链接&#xff08;实体链接&#xff09;与符号链接&#xff08;类似Windows的快捷方式&#xff09;的不同?首先我们应知道&#xff1a;每个档案都会占用一个inode ,档案内容由 inode记录来指向;想要读取该档案&#xff0c;必须要经过目录记录的文件名来指向正确的i…

JAVA--自制斐波那契数列输出

累了&#xff0c;写点简单的。 1 public class hello {2 3 /**4 * param args5 */6 public static void main(String[] args) {7 int Fabnum 10;8 int sum 0;9 System.out.print("Serial:\t"); 10 for(int i 1…

I2C子系统详解4——I2C设备驱动层代码分析

以下内容源于朱有鹏嵌入式课程的学习与整理&#xff0c;如有侵权请告知删除。 一、前言 由I2C总线设备的驱动框架可知&#xff0c;I2C总线设备的驱动框架涉及的文件如下&#xff1a; &#xff08;1&#xff09;I2C设备驱动层相关的文件 x210开发板的电容触摸屏gslX680采用I2C接…

golang java rpc_golang两种调用rpc的方法

本文实例讲述了golang两种调用rpc的方法。分享给大家供大家参考&#xff0c;具体如下&#xff1a;golang的rpc有两种方法进行调用&#xff0c;一种是rpc例子中给的&#xff1a;package mainimport ("net/rpc""net/http""log""net"&quo…

9、C语言 —— 指针的用处

为什么80%的码农都做不了架构师&#xff1f;>>> 1、用函数实现两个数的交换 ‍‍在没用函数之前&#xff0c;可以这样实现‍‍#include <stdio.h>int main() {int a 3;int b 7;int c;printf("交换前&#xff0c;a%d&#xff0c;b%d\n", a, b); …

内核中的竞争状态和互斥(简述)

以下内容源于朱有鹏《物联网大讲堂》课程的学习&#xff0c;如有侵权&#xff0c;请告知删除。 一、概念 &#xff08;1&#xff09;竞争状态&#xff08;简称竟态&#xff09;&#xff1b; &#xff08;2&#xff09;临界段&#xff08;某一段代码&#xff0c;该代码有可能…

MenuetOS

MenuetOS由芬兰人Ville Turjanmaa开发&#xff0c;是一个操作系统&#xff0c;用于和IBM PC兼容的电脑。 它由汇编语言写成&#xff0c;可以存入一只1.44MB的软盘中。 Menuet OS 的32位版本Menuet32使用GNU通用公共许可证发放&#xff0c;但64位版本Menuet64使用自己的协议发放…

php是一种,PHP是一种什么型的语言:()

案例分析一&#xff1a;假定CPU的主频是500MHz。硬盘采用DMA方式进行数据传送&#xff0c;其数据传输率为4MB/s, 每次DMA传输的数据量为8KB, 要求没有任何数据传输被错过。如果CPU在DMA初始化设置和启动硬盘操作等方面用了1000个时钟周期&#xff0c;并且在DMA传送完成后的中断…

java动态代理二cglib

2019独角兽企业重金招聘Python工程师标准>>> java动态代理 转载于:https://my.oschina.net/u/1430510/blog/290215

spring心得6--自动装配知识点讲解及案例分析

1.自动装配&#xff1a; spring3.2以上版本有四种自动装配类型&#xff1a; 1&#xff09;.byName:寻找和属性名相同的bean,若找不到&#xff0c;则装不上。 2&#xff09;.byType:寻找和属性类型相同的bean,找不到,装不上,找到多个抛异常。 3&#xff09;.constructor:按照参数…

中断的上下半部

以下内容源于朱有鹏嵌入式课程的学习与整理&#xff0c;如有侵权请告知删除。 前言 因为输入类设备的输入都是异步事件&#xff0c;因此一般使用中断来处理和响应。 中断处理程序处于中断上下文中&#xff0c;不能和用户空间数据交互&#xff08;不能使用copy_to(from)_usr函数…

arrayPointer

1,分别使用指针加减 int wages[2] {100000000,20000000}; int *pw wages or int *pw &wages[0] 表示指针指向数组的首地址; pw表示地址,*pw表示取值,new分配的动态数组时 指针名称当数组名称使用eg pw[0],pw[2]分别表示指向数组wages的2个数组的元素值; 我们已知pw表示此…

php抓取动态数据,php+ajax实现无刷新动态加载数据技术

我们浏览有些网页的时候&#xff0c;当拉动浏览器的滚动条时到页底时&#xff0c;页面会继续自动加载更多内容供用户浏览。这种技术我暂且称它为滚屏加载技术。我们发现很多网站用到这种技术&#xff0c;必应图片搜索、新浪微博、QQ空间等将该技术应用得淋漓尽致。滚屏加载技术…

图片播放器小项目(详解)

以下内容源于朱有鹏《物联网大讲堂》课程的学习整理&#xff0c;如有侵权&#xff0c;请告知删除。一、开始动手写代码 1、Makefile介绍 &#xff08;1&#xff09;这是一个通用的项目管理的Makefile体系&#xff0c;自己写的&#xff08;有子文件夹组织的&#xff09;项目可以…

基于linux-2.6.32.2的servfox移植

说明&#xff1a;这篇文章是本人在做基于web客户端的远程监控系统课题期间&#xff0c;在移植servfox应用服务程序费了很大周折&#xff0c;所以写下的。 只是介绍了基于arm的servfox移植【因为其他部分的移植没有遇到过错误】 前言&#xff1a; 如何移植基于linux的USB摄像头驱…

Telnet远程访问思科交换机、路由器

一、实验目的Telnet远程访问思科交换机、路由器二、实验拓扑三、实验步骤1、PC1远程管理S11&#xff09;配置交换机的管理IPS1(config)#int vlan 1S1(config-if)#ip add 192.168.1.100 255.255.255.0S1(config-if)#no shu2&#xff09;开启S1的telnet远程管理服务S1(config)#li…

php redis 队列,Redis 实现队列

## Redis 实现队列Redis 实现队列场景说明&#xff1a;用于处理比较耗时的请求&#xff0c;例如批量发送邮件&#xff0c;如果直接在网页触发执行发送&#xff0c;程序会出现超时高并发场景&#xff0c;当某个时刻请求瞬间增加时&#xff0c;可以把请求写入到队列&#xff0c;后…

offsetTop测试见解

offsetTop一开始以为只要容器内div的top定位为负就行&#xff0c;结果今天刚做了个demo&#xff0c;发现好像不是那么回事&#xff0c;于是经过各种测试&#xff0c;得出了一些结论&#xff0c;结论看demo <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" &qu…

[置顶]       cocos2d-x 手游源码站

尊重开发者的劳动成果&#xff0c;转载的时候请务必注明出处&#xff1a;http://blog.csdn.net/haomengzhu/article/details/37829061 1、魔幻方块 链接&#xff1a;魔幻方块源码关键词&#xff1a;魔幻方块源码 源代码 Cocos2d-x2.0 游戏源码 益智 休闲 游戏 游戏类型&#xf…