基于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向应用层提供一个统一标准接口的显示设备.帧缓冲 ...
随机推荐
- Java设计模式-外观模式Facade
介绍 外观模式(Facade),也叫"过程模式:外观模式为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用 外观模式通过定义一个一致的接口, ...
- python-web:flask框架下的html实例——用户注册页面
1.submit实现页面跳转,方法为get <h1>用户注册</h1> <!-- 使用get方式提交,method为post/get,action保存提交到哪里 --&g ...
- 代码+案例,实战解析BeautifulSoup4
本文分享自华为云社区<从HTML到实战:深入解析BeautifulSoup4的爬虫奇妙世界>,作者:柠檬味拥抱. 网络上的信息浩如烟海,而爬虫技术正是帮助我们从中获取有用信息的重要工具.在 ...
- 【Android 抓包对抗】代理检查绕过
1. 安装apk,点进去发现一点就挂 2. apk 拖入到jadx中观察,发现多出检查,一旦满足条件就会退出 .... if (((ConnectivityManager) getSystemServ ...
- [BUUCTF][WEB][极客大挑战 2019]PHP 1
打开靶机URL 看到字面提示 因为每次猫猫都在我键盘上乱跳,所以我有一个良好的备份网站的习惯不愧是我!!! 说明该网站有备份,说不定放在了Http服务器的某个目录下 那么这里我们可以用dirsearc ...
- 【LeetCode二叉树#01】二叉树的遍历(递归/迭代)
二叉树递归遍历 写递归算法时候需要遵循的三个点: 确定递归函数的参数和返回值: 确定哪些参数是递归的过程中需要处理的,那么就在递归函数里加上这个参数, 并且还要明确每次递归的返回值是什么进而确定递归函 ...
- Hello-FPGA CoaXPress 2.0 FPGA DEVICE IP Core Demo
Hello-FPGA CoaXPress 2.0 Device FPGA IP Core Demo 1 说明 本手册针对Helllo-FPGA的CoaXPress 2.0 DEVICE FPG ...
- Spring使用注解方式进行事务管理
目录 使用步骤: 步骤一.在spring配置文件中引入tx:命名空间 步骤二.具有@Transactional 注解的bean自动配置为声明式事务支持 步骤三.在接口或类的声明处 ,写一个@Trans ...
- 【Azure Cache for Redis】Redis的导出页面无法配置Storage SAS时通过az cli来完成
问题描述 在Azure Redis的导出页面,突然不能配置Storage Account的SAS作为授权方式. 那么是否可以通过AZ CLI或者是Powershell来实现SAS的配置呢? 问题解答 ...
- 【Azure Key Vault】使用Azure CLI获取Key Vault 机密遇见问题后使用curl命令来获取机密内容
问题描述 在使用Azure Key Vault的过程中,遇见无法获取机密信息,在不方便直接写代码的情况下,快速使用Azure CLI指令来验证当前使用的认证是否可以获取到正确的机密值. 使用CLI的指 ...