基于rk3588----i2c驱动框架学习(2)-总线驱动 algorithm 分析
rk3588 i2c algorithm 分析
来了来了,上次分析完i2c的驱动框架 今天我们就看看i2c的algorithm是如何实现的
static const struct i2c_algorithm rk3x_i2c_algorithm = {
.master_xfer = rk3x_i2c_xfer,
.master_xfer_atomic = rk3x_i2c_xfer_polling,
.functionality = rk3x_i2c_func,
};
我们就分析master_xfer 函数怎么实现的把。 .functionality比较简单大家可以自己看看了解一下
直接步入正题把。
1.1 i2c时序
首先给大家说一下发送i2c时序的步骤把
- 发送一个其实信号
- 发送7bit设备地址再加1bit读写.最低为1表示读,0为写
- 主机发送完一个从机地址,假设设备是存在的此时为给设备发送一个ACK应答位
- 发送寄存器地址 8bit数据 如果你的数据是 16bit 那就发两次呗。
- 发送一个8bit 等待ACK,在发送一个就在等一个ACK
- 之后发送数据 最多是32位 所以还是要注意的。发送8bit就等一个ACK 这是必须的
- 下来发送一个停止信号。
1.1.1 起始信号
SCL位高 SDA 来一个下降沿 之后就开始通信把。
1.1.2 数据信号
SCL 为高电平,数据有效。
1.1.3 应答信号
SCL为高 的第9bit 为高 则代表有效 如果ACK没有应答 在驱动中则将停止通信了。
1.1.4 停止信号
SCL为高电平 SDA来个上升沿 就结束了。
这样总结很简单把根据这个我们都可以自己手画了一个时序图了。
好了 有了这些 我们就分析驱动吧。
rk3x_i2c_xfer
他调用的式 rk3x_i2c_xfer_common
代码很多我们一句一句看
static int rk3x_i2c_xfer_common(struct i2c_adapter *adap,
struct i2c_msg *msgs, int num, bool polling)
{
struct rk3x_i2c *i2c = (struct rk3x_i2c *)adap->algo_data;
unsigned long timeout, flags;
u32 val;
int ret = 0;
int i;
if (i2c->suspended)
return -EACCES;
spin_lock_irqsave(&i2c->lock, flags); //保存本地中断状态 关闭中断
clk_enable(i2c->clk); //使能了两个时钟
clk_enable(i2c->pclk);
i2c->is_last_msg = false; //把最后一个消息置为否
/*
* Process msgs. We can handle more than one message at once (see
* rk3x_i2c_setup()).
*/
for (i = 0; i < num; i += ret) { //这个num 就是r/w的个数
ret = rk3x_i2c_setup(i2c, msgs + i, num - i); //大概说一下干什么 下面分析 就是判断寄存器地址是否为16bit
if (ret < 0) {
dev_err(i2c->dev, "rk3x_i2c_setup() failed\n");
break;
}
if (i + ret >= num) //判断 当前i+ret(已经传输的个数如果大于) num了 那么就是最后一个信号了 是不是停止信号呢?
i2c->is_last_msg = true;
rk3x_i2c_start(i2c); // 下面分析 大概说一下 就是配置接收发送中断
spin_unlock_irqrestore(&i2c->lock, flags);
if (!polling) { //这个是是否阻塞 如果超时阻塞 那么等待唤醒
timeout = wait_event_timeout(i2c->wait, !i2c->busy,
msecs_to_jiffies(WAIT_TIMEOUT));
} else { //这个暂时不分析了
timeout = rk3x_i2c_wait_xfer_poll(i2c);
}
spin_lock_irqsave(&i2c->lock, flags);
if (timeout == 0) { //返回0了 那就超时了
dev_err(i2c->dev, "timeout, ipd: 0x%02x, state: %d\n",
i2c_readl(i2c, REG_IPD), i2c->state);
/* Force a STOP condition without interrupt */
rk3x_i2c_disable_irq(i2c); //关闭中断
val = i2c_readl(i2c, REG_CON) & REG_CON_TUNING_MASK; //读取 i2c控制器控制寄存器 保存 是否停止配置设置寄存器
val |= REG_CON_EN | REG_CON_STOP; //0x1001 意思是使能i2c模块 并产生一个i2c停止信号
i2c_writel(i2c, val, REG_CON); //写入控制寄存器
i2c->state = STATE_IDLE; //设置为空闲状态
ret = -ETIMEDOUT; //返回超时错误码
break;
}
if (i2c->error) {
ret = i2c->error;
break;
}
}
rk3x_i2c_disable_irq(i2c); //禁用中断
rk3x_i2c_disable(i2c); //除了 保存停止配置设置寄存器的值 剩下都置为0 意思就是把所有都关闭了
clk_disable(i2c->pclk); //关闭时钟
clk_disable(i2c->clk);
spin_unlock_irqrestore(&i2c->lock, flags);
return ret < 0 ? ret : num;
}
看一下 rk3x_i2c_setup
static int rk3x_i2c_setup(struct rk3x_i2c *i2c, struct i2c_msg *msgs, int num)
{
u32 addr = (msgs[0].addr & 0x7f) << 1; //地址左移1bit 这不就和协议对上了 7bit+r/w 位
int ret = 0;
/*
* The I2C adapter can issue a small (len < 4) write packet before
* reading. This speeds up SMBus-style register reads.
* The MRXADDR/MRXRADDR hold the slave address and the slave register
* address in this case.
*/
if (num >= 2 && msgs[0].len < 4 &&
!(msgs[0].flags & I2C_M_RD) && (msgs[1].flags & I2C_M_RD)) { //就是16bit的寄存器
u32 reg_addr = 0;
int i;
dev_dbg(i2c->dev, "Combined write/read from addr 0x%x\n",
addr >> 1);
/* Fill MRXRADDR with the register address(es) */
for (i = 0; i < msgs[0].len; ++i) {
reg_addr |= msgs[0].buf[i] << (i * 8);
reg_addr |= REG_MRXADDR_VALID(i);
} //硬件操作发送16bit寄存器地址
/* msgs[0] is handled by hw. */
i2c->msg = &msgs[1];
i2c->mode = REG_CON_MOD_REGISTER_TX;
i2c_writel(i2c, addr | REG_MRXADDR_VALID(0), REG_MRXADDR); //下来发送数据
i2c_writel(i2c, reg_addr, REG_MRXRADDR);
ret = 2;
} else {
/*
* We'll have to do it the boring way and process the msgs
* one-by-one.
*/
if (msgs[0].flags & I2C_M_RD) {
addr |= 1; /* set read bit */ //设置了一个读位
/*
* We have to transmit the slave addr first. Use
* MOD_REGISTER_TX for that purpose.
*/
i2c->mode = REG_CON_MOD_REGISTER_TX;
i2c_writel(i2c, addr | REG_MRXADDR_VALID(0),
REG_MRXADDR);
i2c_writel(i2c, 0, REG_MRXRADDR);
} else {
i2c->mode = REG_CON_MOD_TX;
}
i2c->msg = &msgs[0];
ret = 1;
}
i2c->addr = msgs[0].addr;
i2c->busy = true;
i2c->processed = 0;
i2c->error = 0;
rk3x_i2c_clean_ipd(i2c);
if (i2c->autostop_supported)
i2c_writel(i2c, 0, REG_CON1);
return ret;
}
看一下 rk3x_i2c_start
static void rk3x_i2c_start(struct rk3x_i2c *i2c)
{
u32 val = i2c_readl(i2c, REG_CON) & REG_CON_TUNING_MASK;
bool auto_stop = rk3x_i2c_auto_stop(i2c);
int length = 0;
/* enable appropriate interrupts */
if (i2c->mode == REG_CON_MOD_TX) {
if (!auto_stop) { //使能发送中断
i2c_writel(i2c, REG_INT_MBTF | REG_INT_NAKRCV, REG_IEN);
i2c->state = STATE_WRITE;
}
length = rk3x_i2c_fill_transmit_buf(i2c, false);
} else {
/* in any other case, we are going to be reading. */
if (!auto_stop) {
i2c_writel(i2c, REG_INT_MBRF | REG_INT_NAKRCV, REG_IEN); //使能接受中断
i2c->state = STATE_READ;
}
}
/* enable adapter with correct mode, send START condition */
val |= REG_CON_EN | REG_CON_MOD(i2c->mode) | REG_CON_START;
/* if we want to react to NACK, set ACTACK bit */
if (!(i2c->msg->flags & I2C_M_IGNORE_NAK))
val |= REG_CON_ACTACK;
i2c_writel(i2c, val, REG_CON);
/* enable transition */
if (i2c->mode == REG_CON_MOD_TX)
i2c_writel(i2c, length, REG_MTXCNT); //使能接受或者发送
else
rk3x_i2c_prepare_read(i2c);
}
差一个irq 明天继续 记录一下思路 开启中断后 每次 将会填充 发送或者接受 数据寄存器 当满了之后呢么保存数据 或者发送 都保存在 msg->buf 里了 这就是所谓了i2c通信算法 就是根据协议去写 更清楚的了解了 i2c协议
基于rk3588----i2c驱动框架学习(2)-总线驱动 algorithm 分析的更多相关文章
- RT Thread的SPI设备驱动框架的使用以及内部机制分析
注释:这是19年初的博客,写得很一般,理解不到位也不全面.19年末得空时又重新看了RTThread的SPI和GPIO,这次理解得比较深刻.有时间时再整理上传. -------------------- ...
- Android 框架学习2:源码分析 EventBus 3.0 如何实现事件总线
Go beyond yourself rather than beyond others. 上篇文章 深入理解 EventBus 3.0 之使用篇 我们了解了 EventBus 的特性以及如何使用,这 ...
- Linux 驱动框架---cdev字符设备驱动和misc杂项设备驱动
字符设备 Linux中设备常见分类是字符设备,块设备.网络设备,其中字符设备也是Linux驱动中最常用的设备类型.因此开发Linux设备驱动肯定是要先学习一下字符设备的抽象的.在内核中使用struct ...
- Linux 驱动框架---i2c驱动框架
i2c驱动在Linux通过一个周的学习后发现i2c总线的驱动框架还是和Linux整体的驱动框架是相同的,思想并不特殊比较复杂的内容如i2c核心的内容都是内核驱动框架实现完成的,今天我们暂时只分析驱动开 ...
- I2C驱动框架(kernel-2.6.22.6)
以用i2c通信的实时时钟为例 框架入口源文件:i2c_m41t11.c (可根据入口源文件,再按着框架到内核走一遍) 内核版本:linux_2.6.22.6 硬件平台:JZ2440 以下是驱动框架 ...
- spi驱动框架全面分析,从master驱动到设备驱动
内核版本:linux2.6.32.2 硬件资源:s3c2440 参考: 韦东山SPI视频教程 内容概括: 1.I2C 驱动框架回顾 2.SPI 框架简单介绍 3.maste ...
- 驱动框架入门——以LED为例[【转】
本文转载自;http://blog.csdn.net/oqqHuTu12345678/article/details/72783903 以下内容源于朱有鹏<物联网大讲堂>课程的学习,如有侵 ...
- lcd驱动框架
目录 lcd驱动框架 框图 程序分析 入口 打开open 读read 初始化registered_fb 注册 小结 程序设计 测试 方式一操作fb0 方式二操作tty 方式三操作终端 完整程序 tit ...
- linux-2.6.38 IIC驱动框架分析
在linux-2.6内核中,IIC的驱动程序可以大概分为三部分: (1)IIC核心代码:/drivers/i2c/i2c-core.c IIC核心提供了IIC总线驱动和设备驱动的注册.注销方法和IIC ...
- Linux驱动框架之framebuffer驱动框架
1.什么是framebuffer? (1)framebuffer帧缓冲(一屏幕数据)(简称fb)是linux内核中虚拟出的一个设备,framebuffer向应用层提供一个统一标准接口的显示设备.帧缓冲 ...
随机推荐
- 基于角色的权限控制(RBAC)介绍
什么是RBAC? RBAC(Role-Based Access Control)基于角色的权限控制.其基本思想是,对系统操作的各种权限不是直接授予具体的用户,而是在用户集合与权限集合之间建立一个角色集 ...
- 使用Gulp压缩静态资源
如果希望对在静态页面中引入的相关资源进行压缩(比如:CSS,JavaScript,图片等),可以使用Gulp实现. 当然,还可以使用其他打包工具,比如:Grunt,Webpack等等. Gulp是什么 ...
- 硬件开发笔记(九): 硬件开发基本流程,制作一个USB转RS232的模块(八):创建asm1117-3.3V封装库并关联原理图元器件
前言 有了原理图,可以设计硬件PCB,在设计PCB之间还有一个协同优先动作,就是映射封装,原理图库的元器件我们是自己设计的.为了更好的表述封装设计过程,本文描述了一个创建asm1117-3.3V封 ...
- Celey异步发送邮件时报django.core.exceptions.ImproperlyConfigured的解决办法
原main.py入口文件 #Celery的入口 from celery import Celery #创建Celery实例 生产者 celery_app = Celery('meiduo') #加载配 ...
- Redis加Lua脚本实现分布式锁
先讲一下为什么使用分布式锁: 在传统的单体应用中,我们可以使用Java并发处理相关的API(如ReentrantLock或synchronized)来实现对共享资源的互斥控制,确保在高并发情况下同一时 ...
- 第132篇:npm第一次使用自己的包(package-lock.json、package.json文件作用说明)
好家伙, 1.新建一个文件夹,命名为test 2.下载包 npm i panghu-planebattle 空白的文件夹中多了两个文件 package-lock.json和package. ...
- 【Azure 事件中心】Event Hub服务中的度量值指标介绍
问题描述 Event Hub服务中的度量值指标解说 1)request和message的区别 2)capture backlog 和 capture message 怎么理解 3)quota exce ...
- Java //数组的反转
1 //数组的反转 2 //方式一 3 System.out.println("数组的反转"); 4 5 // for(int i = 0; i <arr.length/2; ...
- linux下,使用nginx实现动静分离,访问图片报404
一.需求描述 最近在开发一个微信小程序,由于微信小程序端代码包总大小限定在三四兆,所以有很多的图标资源就不能放在微信小程序中进行打包, 否则会超过微信的限制而无法打包.自己能够想到的最简单的办法就是将 ...
- nowrap - table td 列 宽度 不被挤 - 大表格制作
nowrap - table td 列 宽度 不被挤 - 大表格制作 表格前几列 设置完宽度,会被右侧动态数据挤没有宽度,加上nowrap,就保证宽度了