当前位置:网站首页>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()函数;
边栏推荐
- 大规模服务异常日志检索
- MVC模式和三层架构
- TP configuring multiple databases
- New technology releases a small program UNIPRO to meet customers' mobile office scenarios
- kaili不能输入中文怎么办???
- 7 RSA密码体制
- [acwing] 58 weeks 4489 Longest subsequence
- leetcode:421. 数组中两个数的最大异或值
- 超标量处理器设计 姚永斌 第7章 寄存器重命名 摘录
- Electronic pet dog - what is the internal structure?
猜你喜欢
VB cannot access database stocks
Rainfall warning broadcast automatic data platform bwii broadcast warning monitor
Master the use of auto analyze in data warehouse
【Unity UGUI】ScrollRect 动态缩放格子大小,自动定位到中间的格子
斑马识别成狗,AI犯错的原因被斯坦福找到了丨开源
Developers, MySQL column finish, help you easily from installation to entry
Hidden corners of coder Edition: five things that developers hate most
【Go ~ 0到1 】 第六天 文件的读写与创建
Firewall basic transparent mode deployment and dual machine hot standby
补能的争议路线:快充会走向大一统吗?
随机推荐
leetcode刷题目录总结
大规模服务异常日志检索
离线、开源版的 Notion—— 笔记软件Anytype 综合评测
你应该懂些CI/CD
leetcode:421. 数组中两个数的最大异或值
【HCIA持续更新】广域网技术
Two methods of MD5 encryption
Leetcode list summary
R language plot visualization: plot visualizes overlapping histograms and uses geom at the top edge of the histogram_ The rug function adds marginal rug plots
tx.origin安全问题总结
什么是低代码开发?
【HCIA持续更新】WLAN工作流程概述
Linear time sequencing
智捷云——元宇宙综合解决方案服务商
中银证券网上开户安全吗?
Dynamic programming stock problem comparison
Win32 API 访问路由的加密网页
Great Wall Securities security does not open a securities account
detectron2安装方法
【HCIA持续更新】网络管理与运维