当前位置:网站首页>S5PV210芯片I2C适配器驱动分析(i2c-s3c2410.c)
S5PV210芯片I2C适配器驱动分析(i2c-s3c2410.c)
2022-07-04 15:58:00 【正在起飞的蜗牛】
1、什么是适配器驱动
(1)适配器驱动就是用来控制Soc上的I2C控制器的,封装I2C控制器的通信方式;
(2)适配器驱动会向I2C核心层注册,I2C核心层会管理内核中所有注册的适配器驱动,每一个注册的适配器驱动就代表Soc中的一个I2C控制器;
(3)I2C接口设备的驱动会通过I2C总线匹配上对应的适配器,然后调用适配器提供的数据收发接口进行通信;
2、适配器驱动怎么和Soc上的I2C控制器对应
/*********gslX680.c**************/
#define GSLX680_I2C_NAME "gslX680"
static struct i2c_driver gsl_ts_driver = {
.driver = {
.name = GSLX680_I2C_NAME,
.owner = THIS_MODULE,
},
#ifndef CONFIG_HAS_EARLYSUSPEND
.suspend = gsl_ts_suspend,
.resume = gsl_ts_resume,
#endif
.probe = gsl_ts_probe,
.remove = __devexit_p(gsl_ts_remove),
.id_table = gsl_ts_id,
};
//函数调用关系
gsl_ts_init() //驱动加载函数
i2c_add_driver(&gsl_ts_driver); //向I2C总线注册gslX680驱动
gsl_ts_probe() //当gslX680驱动在I2C总线上匹配上struct i2c_client时就会调用probe方法
/*********内核注册i2c_board_info信息************/
static struct i2c_board_info i2c_devs1[] __initdata = {
{
I2C_BOARD_INFO("gslX680", 0x40), //gslX680是用来和驱动匹配的名字,0x40是设备在I2C总线上的地址
},
};
smdkc110_machine_init() //struct machine_desc->init_machine()
i2c_register_board_info(1, i2c_devs1, ARRAY_SIZE(i2c_devs1)); //向编号是1的适配器注册i2c_board_info信息
(1)上面是gslX680触摸屏驱动代码和在I2C总线上的匹配相关部分的代码;
(2)struct i2c_board_info结构体:会用来构成struct i2c_client结构体,将来I2C接口设备的驱动在I2C总线上匹配设备时,就会用名字来进行匹配,
哪一个适配器上有该驱动的名字就会和哪个适配器匹配成功,并且还会把struct i2c_board_info结构体里的信息传给I2C驱动,比如:设备地址、中断号;
(3)每个适配器都会注册struct i2c_board_info结构体信息,将来struct i2c_board_info结构体里的名字会和I2C驱动的名字进行匹配, I2C驱动和适配器匹配上,
将来I2C驱动通信就通过匹配上的适配器,也就是硬件上I2C接口设备接在哪一个I2C控制器;
3、适配器驱动的加载过程
static struct platform_device_id s3c24xx_driver_ids[] = {
{
.name = "s3c2410-i2c",
.driver_data = TYPE_S3C2410,
}, {
.name = "s3c2440-i2c",
.driver_data = TYPE_S3C2440,
}, {
},
};
static struct platform_driver s3c24xx_i2c_driver = {
.probe = s3c24xx_i2c_probe,
.remove = s3c24xx_i2c_remove,
.id_table = s3c24xx_driver_ids, //在platform总线上匹配设备时使用
.driver = {
.owner = THIS_MODULE,
.name = "s3c-i2c",
.pm = S3C24XX_DEV_PM_OPS,
},
};
i2c_adap_s3c_init() //驱动加载函数
platform_driver_register(&s3c24xx_i2c_driver); //利用平台总线进行注册
s3c24xx_i2c_probe() //在平台总线上匹配上设备后调用probe函数
i2c_add_numbered_adapter(&i2c->adap); //利用platform_device传过来的数据构建适配器结构体,并向I2C核心层注册
(1) 在内核的struct machine_desc->init_machine()函数中会注册plat_device,会有多个plat_device和platform总线驱动s3c24xx_i2c_driver匹配上,
基本上是Soc有几个I2C控制器就匹配上几次,也就是会向I2C核心层注册多个适配器;
(2)虽然被匹配上多次,但是每次plat_device传过来的数据都是不同的,包括I2C控制器的寄存器物理地址、中断号、适配器编号等;
4、I2C总线驱动描述结构体
struct s3c24xx_i2c {
spinlock_t lock;
wait_queue_head_t wait; //为了实现同步,使i2c->algorithm->master_xfer函数能在中断中的传输过程完成时返回
unsigned int suspended:1;
//记录待传输数据的信息
struct i2c_msg *msg; //消息队列
unsigned int msg_num; //消息队列中的消息数
unsigned int msg_idx; //当前传输的消息是序列中的第几条
unsigned int msg_ptr; //传输的是当前的第几个字节
unsigned int tx_setup; //传输建立延时
unsigned int irq; //使用的中断号
enum s3c24xx_i2c_state state; //当前传输的状态,进行到哪一步
unsigned long clkrate;
void __iomem *regs; //用于访问内核IO内存的实际地址
struct clk *clk;
struct device *dev;
struct resource *ioarea; //IO内存资源
struct i2c_adapter adap; //对应的适配器
};
5、适配器驱动的probe方法实现
static int s3c24xx_i2c_probe(struct platform_device *pdev)
{
struct s3c24xx_i2c *i2c;
struct s3c2410_platform_i2c *pdata;
struct resource *res;
int ret;
//解析处platform总线设备传递的信息
pdata = pdev->dev.platform_data;
i2c = kzalloc(sizeof(struct s3c24xx_i2c), GFP_KERNEL);
//设置适配器的名字
strlcpy(i2c->adap.name, "s3c2410-i2c", sizeof(i2c->adap.name));
//构建适配器结构体adap
i2c->adap.owner = THIS_MODULE;
i2c->adap.algo = &s3c24xx_i2c_algorithm; //设置适配器的通信方法
i2c->adap.retries = 2; //重发次数是2
i2c->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
i2c->tx_setup = 50;
//初始化自旋锁和等待队列
spin_lock_init(&i2c->lock);
init_waitqueue_head(&i2c->wait);
/* 获取I2C控制器的时钟并使能 */
i2c->dev = &pdev->dev;
i2c->clk = clk_get(&pdev->dev, "i2c"); //获取时钟系统给I2C控制器的提供的时钟频率
clk_enable(i2c->clk); //使能时钟
/* 获取I2C控制器的IO地址资源 */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
······
//申请寄存器的物理地址
i2c->ioarea = request_mem_region(res->start, resource_size(res),
pdev->name);
//动态映射物理地址
i2c->regs = ioremap(res->start, resource_size(res));
//在master_xfer等方法中就能通过适配器的algo_data获取对应的i2c对象
i2c->adap.algo_data = i2c;
i2c->adap.dev.parent = &pdev->dev;
//初始化芯片的I2C控制器:使能中断、ACK使能、设置对应的GPIO、初始化时钟等
ret = s3c24xx_i2c_init(i2c);
if (ret != 0)
goto err_iomap;
//获取中断号资源
i2c->irq = ret = platform_get_irq(pdev, 0);
if (ret <= 0) {
dev_err(&pdev->dev, "cannot find IRQ\n");
goto err_iomap;
}
//申请中断号并绑定中断处理程序s3c24xx_i2c_irq
ret = request_irq(i2c->irq, s3c24xx_i2c_irq, IRQF_DISABLED,
dev_name(&pdev->dev), i2c);
//利用内核通知链机制,当CPU频率变化时,时I2C时钟设置能做出调整
ret = s3c24xx_i2c_register_cpufreq(i2c);
if (ret < 0) {
dev_err(&pdev->dev, "failed to register cpufreq notifier\n");
goto err_irq;
}
//从platform总线设备获得适配器的总线编号
i2c->adap.nr = pdata->bus_num;
//向I2C核心层注册适配器
ret = i2c_add_numbered_adapter(&i2c->adap);
if (ret < 0) {
dev_err(&pdev->dev, "failed to add bus to i2c core\n");
goto err_cpufreq;
}
//将i2c保存到pdev->dev->p->driver_data
platform_set_drvdata(pdev, i2c);
clk_disable(i2c->clk);
dev_info(&pdev->dev, "%s: S3C I2C adapter\n", dev_name(&i2c->adap.dev));
return 0;
······
}
6、I2C总线通信方法
6.1、适配器的通信方法
static const struct i2c_algorithm s3c24xx_i2c_algorithm = {
.master_xfer = s3c24xx_i2c_xfer,
.functionality = s3c24xx_i2c_func,
};
6.2、数据通信的函数调用关系
s3c24xx_i2c_xfer() //adap->algo->master_xfer
s3c24xx_i2c_doxfer()
s3c24xx_i2c_set_master() //确保当前I2C控制器处于空闲,才继续下面的操作
s3c24xx_i2c_enable_irq() //使能i2C-Bus的Tx/Rx中断
s3c24xx_i2c_message_start() //开启I2C通信
wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5); //发送函数进入等待,直到消息发送完成或者超过5s返回
在中断程序中唤醒等待队列:
s3c24xx_i2c_irq() //适配器绑定的中断处理函数
i2c_s3c_irq_nextbyte() //发送数据
s3c24xx_i2c_stop() //当数据收发完成或者通信出错时停止本次传输
s3c24xx_i2c_master_complete() //本次传输完成
wake_up(&i2c->wait); //唤醒等待队列
(1)I2C适配器描述结构体struct i2c_adapter中algo变量是适配器的通信方法,在驱动程序的prob函数中赋值;
(2)通信是以消息(struct i2c_msg)为单位进行的,消息描述结构体会指明要发送的设备的地址、数据传输方向等;
6.3、通信过程
(1)首先是I2C设备驱动调用适配器的adap->algo->master_xfer进行发送消息,master_xfer方法在进行一些初始化后,开启本次I2C通信,然后就进入等待队列,直到消息发送完成或者5秒超时后返回;
(2)具体的发送是在probe方法绑定了中断函数中进行,这个中断函数是I2C控制器的中断处理函数,在发送完数据或者出错后,就会唤醒之前在等待s3c24xx_i2c_doxfer()函数;
边栏推荐
- 【Go ~ 0到1 】 第六天 文件的读写与创建
- Kunming Third Ring Road Closure project will pass through these places. Is there one near your home?
- CANN算子:利用迭代器高效实现Tensor数据切割分块处理
- Chow Tai Fook fulfills the "centenary commitment" and sincerely serves to promote green environmental protection
- MD5加密的两种方式
- What grade does Anxin securities belong to? Is it safe to open an account
- The Block:USDD增长势头强劲
- With an annual income of more than 8 million, he has five full-time jobs. He still has time to play games
- Analysis of abnormal frequency of minor GC in container environment
- 2022PMP考试基本情况详情了解
猜你喜欢
【Go ~ 0到1 】 第六天 文件的读写与创建
斑马识别成狗,AI犯错的原因被斯坦福找到了丨开源
整理混乱的头文件,我用include what you use
利用win10计划任务程序定时自动运行jar包
Offline and open source version of notation -- comprehensive evaluation of note taking software anytype
Which domestic cloud management platform manufacturer is good in 2022? Why?
整理混乱的头文件,我用include what you use
Difference between redis' memory obsolescence strategy and expiration deletion strategy
第十八届IET交直流输电国际会议(ACDC2022)于线上成功举办
Learn more about the basic situation of 2022pmp examination
随机推荐
Vb无法访问数据库stocks
Flask 轻量web框架
detectron2安装方法
Solution of dealer collaboration system in building materials industry: empowering enterprises to build core competitiveness
Solve the El input input box For number number input problem, this method can also be used to replace the problem of removing the arrow after type= "number"
Go micro tutorial - Chapter 2 go micro V3 using gin and etcd
电子宠物小狗-内部结构是什么?
Implementation of super large-scale warehouse clusters in large commercial banks
Solution of commercial supply chain coordination system in the mineral industry: build a digital intelligent supply chain platform to ensure the safe supply of mineral resources
Cocoscreator event dispatch use
缓存穿透、缓存击穿、缓存雪崩分别是什么
CocosCreator事件派发使用
leetcode刷题目录总结
安信证券手机版下载 网上开户安全吗
图像检索(image retrieval)
[glide] cache implementation - memory and disk cache
Pytorch深度学习之环境搭建
Ble HCI flow control mechanism
Two methods of MD5 encryption
网页游戏引擎