我对linux理解之i2c 二
linux-dash
A beautiful web dashboard for Linux
项目地址:https://gitcode.com/gh_mirrors/li/linux-dash
免费下载资源
·
我们下面开始分析i2c的通信,即读写过程。
我们先看读函数,对应i2c core中的i2c_master_recv:
int i2c_master_recv(struct i2c_client *client, char *buf ,int count)
{
struct i2c_adapter *adap=client->adapter;//通过client访问adapter
struct i2c_msg msg;//i2c传输的单位
int ret;
msg.addr = client->addr;
msg.flags = client->flags & I2C_M_TEN;
msg.flags |= I2C_M_RD;//读标记,如果没有就是写
msg.len = count;//数据字节数
msg.buf = buf;//数据保存的地方
ret = i2c_transfer(adap, &msg, 1);//传输1个msg
/* If everything went ok (i.e. 1 msg transmitted), return #bytes
transmitted, else error code. */
return (ret == 1) ? count : ret;
}
在看下写函数,对应i2c core中的i2c_master_send:
int i2c_master_send(struct i2c_client *client,const char *buf ,int count)
{
int ret;
struct i2c_adapter *adap=client->adapter;//通过client访问adapter
struct i2c_msg msg;//这个就是i2c传输的单位
msg.addr = client->addr;
msg.flags = client->flags & I2C_M_TEN;//没有读标记就代表写操作
msg.len = count;//数据字节数
msg.buf = (char *)buf;//要写的buf
ret = i2c_transfer(adap, &msg, 1);//传输1个msg
/* If everything went ok (i.e. 1 msg transmitted), return #bytes
transmitted, else error code. */
return (ret == 1) ? count : ret;
}
我们发现无论读还是写函数最终都使用i2c_transfer()进行传输msg,下面具体分析一下i2c_transfer:
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
unsigned long orig_jiffies;
int ret, try;
/* REVISIT the fault reporting model here is weak:
*
* - When we get an error after receiving N bytes from a slave,
* there is no way to report "N".
*
* - When we get a NAK after transmitting N bytes to a slave,
* there is no way to report "N" ... or to let the master
* continue executing the rest of this combined message, if
* that's the appropriate response.
*
* - When for example "num" is two and we successfully complete
* the first message but get an error part way through the
* second, it's unclear whether that should be reported as
* one (discarding status on the second message) or errno
* (discarding status on the first one).
*/
if (adap->algo->master_xfer) {//显然这个i2c_transfer依赖i2c_algorithm中的master_xfer是否被定义,我们在注册分析中知道它已经被赋值为.master_xfer = mxc_i2c_xfer
#ifdef DEBUG
for (ret = 0; ret < num; ret++) {
dev_dbg(&adap->dev, "master_xfer[%d] %c, addr=0x%02x, "
"len=%d%s\n", ret, (msgs[ret].flags & I2C_M_RD)
? 'R' : 'W', msgs[ret].addr, msgs[ret].len,
(msgs[ret].flags & I2C_M_RECV_LEN) ? "+" : "");
}
#endif
if (in_atomic() || irqs_disabled()) {
ret = mutex_trylock(&adap->bus_lock);
if (!ret)
/* I2C activity is ongoing. */
return -EAGAIN;
} else {
mutex_lock_nested(&adap->bus_lock, adap->level);//给bus上锁了
}
/* Retry automatically on arbitration loss */
orig_jiffies = jiffies;
for (ret = 0, try = 0; try <= adap->retries; try++) {
ret = adap->algo->master_xfer(adap, msgs, num);//最终转换为i2c_algorithm中的master_xfer传输
if (ret != -EAGAIN)
break;
if (time_after(jiffies, orig_jiffies + adap->timeout))//retry间隔时间
break;
}
mutex_unlock(&adap->bus_lock);
return ret;
} else {
dev_dbg(&adap->dev, "I2C level transfers not supported\n");
return -EOPNOTSUPP;
}
}
我们在注册分析中知道adap->algo被赋值为&mxc_i2c_algorithm,而mxc_i2c_algorithm定义为:
static struct i2c_algorithm mxc_i2c_algorithm = {
.master_xfer = mxc_i2c_xfer,//传输函数
.functionality = mxc_i2c_func//功能支持
};
所以adap->algo->master_xfer实际为mxc_i2c_xfer,这个函数即为i2c控制器的传输函数:
static int mxc_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
int num)
{
mxc_i2c_device *dev = (mxc_i2c_device *) (i2c_get_adapdata(adap));
int i, ret = 0, addr_comp = 0;
volatile unsigned int sr;
int retry = 5, retry_start = 5;
retry:
if (dev->low_power) {
dev_err(&dev->adap.dev, "I2C Device in low power mode\n");
return -EREMOTEIO;
}
if (num < 1) {
return 0;
}
mxc_i2c_module_en(dev, msgs[0].flags);
sr = readw(dev->membase + MXC_I2SR);//读i2c的状态寄存器
/*
* Check bus state
*/
while ((sr & MXC_I2SR_IBB) && retry--) {//检查i2c总线状态是否处于忙
udelay(5);
sr = readw(dev->membase + MXC_I2SR);
}
if ((sr & MXC_I2SR_IBB) && retry < 0) {
mxc_i2c_module_dis(dev);
dev_err(&dev->adap.dev, "Bus busy\n");
return -EREMOTEIO;
}
//gpio_i2c_active(dev->adap.id);
dev->transfer_done = false;
dev->tx_success = false;
for (i = 0; i < num && ret >= 0; i++) {//num是要传输的msg个数
addr_comp = 0;
/*
* Send the slave address and transfer direction in the
* address cycle
*/
if (i == 0) {
/*
* Send a start or repeat start signal
*/
if (mxc_i2c_start(dev, &msgs[0])){//发送开始信号
if(retry_start-- != 0)
goto retry;
return -EREMOTEIO;
}
/* Wait for the address cycle to complete */
if (mxc_i2c_wait_for_tc(dev, msgs[0].flags)) {//等待地址周期完成
mxc_i2c_stop(dev);
//gpio_i2c_inactive(dev->adap.id);
mxc_i2c_module_dis(dev);
return -EREMOTEIO;
}
addr_comp = 1;
} else {
/*
* Generate repeat start only if required i.e the address
* changed or the transfer direction changed
*/
if ((msgs[i].addr != msgs[i - 1].addr) ||
((msgs[i].flags & I2C_M_RD) !=
(msgs[i - 1].flags & I2C_M_RD))) {//从if条件可以看出是判断地址和读写方向
mxc_i2c_repstart(dev, &msgs[i]);
/* Wait for the address cycle to complete */
if (mxc_i2c_wait_for_tc(dev, msgs[i].flags)) {
mxc_i2c_stop(dev);
//gpio_i2c_inactive(dev->adap.id);
mxc_i2c_module_dis(dev);
return -EREMOTEIO;
}
addr_comp = 1;
}
}
/* Transfer the data */
if (msgs[i].flags & I2C_M_RD) {//根据读写标记决定是读操作还是写操作
/* Read the data */
ret = mxc_i2c_readbytes(dev, &msgs[i], (i + 1 == num),
addr_comp);
if (ret < 0) {
dev_err(&dev->adap.dev, "mxc_i2c_readbytes:"
" fail.\n");
break;
}
} else {
/* Write the data */
ret = mxc_i2c_writebytes(dev, &msgs[i], (i + 1 == num));
if (ret < 0) {
dev_err(&dev->adap.dev, "mxc_i2c_writebytes:"
" fail.\n");
break;
}
}
}
//gpio_i2c_inactive(dev->adap.id);
mxc_i2c_module_dis(dev);
/*
* Decrease by 1 as we do not want Start message to be included in
* the count
*/
return (i < 0 ? ret : i);
}
我们看到控制器的传输函数是按照i2c协议的传输过程。在这里我们要看下mxc_i2c_start(dev, &msgs[0]):
static int mxc_i2c_start(mxc_i2c_device *dev, struct i2c_msg *msg)
{
volatile unsigned int cr, sr;
unsigned int addr_trans;
int retry = 16;
retry:
/*
* Set the slave address and the requested transfer mode
* in the data register
*/
addr_trans = msg->addr << 1;//地址x2,所以设备开始设置地址时要设为写地址的1/2
if (msg->flags & I2C_M_RD) {//如果是读操作,这地址加1
addr_trans |= 0x01;
}
......
}
我们注意一下这里的地址转换,所以设备信息设置的时候注意一下地址大小需要除以2 。
上面就是i2c读写过程:driver->i2c_core->adapter
我们先看读函数,对应i2c core中的i2c_master_recv:
int i2c_master_recv(struct i2c_client *client, char *buf ,int count)
{
struct i2c_adapter *adap=client->adapter;//通过client访问adapter
struct i2c_msg msg;//i2c传输的单位
int ret;
msg.addr = client->addr;
msg.flags = client->flags & I2C_M_TEN;
msg.flags |= I2C_M_RD;//读标记,如果没有就是写
msg.len = count;//数据字节数
msg.buf = buf;//数据保存的地方
ret = i2c_transfer(adap, &msg, 1);//传输1个msg
/* If everything went ok (i.e. 1 msg transmitted), return #bytes
transmitted, else error code. */
return (ret == 1) ? count : ret;
}
在看下写函数,对应i2c core中的i2c_master_send:
int i2c_master_send(struct i2c_client *client,const char *buf ,int count)
{
int ret;
struct i2c_adapter *adap=client->adapter;//通过client访问adapter
struct i2c_msg msg;//这个就是i2c传输的单位
msg.addr = client->addr;
msg.flags = client->flags & I2C_M_TEN;//没有读标记就代表写操作
msg.len = count;//数据字节数
msg.buf = (char *)buf;//要写的buf
ret = i2c_transfer(adap, &msg, 1);//传输1个msg
/* If everything went ok (i.e. 1 msg transmitted), return #bytes
transmitted, else error code. */
return (ret == 1) ? count : ret;
}
我们发现无论读还是写函数最终都使用i2c_transfer()进行传输msg,下面具体分析一下i2c_transfer:
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
unsigned long orig_jiffies;
int ret, try;
/* REVISIT the fault reporting model here is weak:
*
* - When we get an error after receiving N bytes from a slave,
* there is no way to report "N".
*
* - When we get a NAK after transmitting N bytes to a slave,
* there is no way to report "N" ... or to let the master
* continue executing the rest of this combined message, if
* that's the appropriate response.
*
* - When for example "num" is two and we successfully complete
* the first message but get an error part way through the
* second, it's unclear whether that should be reported as
* one (discarding status on the second message) or errno
* (discarding status on the first one).
*/
if (adap->algo->master_xfer) {//显然这个i2c_transfer依赖i2c_algorithm中的master_xfer是否被定义,我们在注册分析中知道它已经被赋值为.master_xfer = mxc_i2c_xfer
#ifdef DEBUG
for (ret = 0; ret < num; ret++) {
dev_dbg(&adap->dev, "master_xfer[%d] %c, addr=0x%02x, "
"len=%d%s\n", ret, (msgs[ret].flags & I2C_M_RD)
? 'R' : 'W', msgs[ret].addr, msgs[ret].len,
(msgs[ret].flags & I2C_M_RECV_LEN) ? "+" : "");
}
#endif
if (in_atomic() || irqs_disabled()) {
ret = mutex_trylock(&adap->bus_lock);
if (!ret)
/* I2C activity is ongoing. */
return -EAGAIN;
} else {
mutex_lock_nested(&adap->bus_lock, adap->level);//给bus上锁了
}
/* Retry automatically on arbitration loss */
orig_jiffies = jiffies;
for (ret = 0, try = 0; try <= adap->retries; try++) {
ret = adap->algo->master_xfer(adap, msgs, num);//最终转换为i2c_algorithm中的master_xfer传输
if (ret != -EAGAIN)
break;
if (time_after(jiffies, orig_jiffies + adap->timeout))//retry间隔时间
break;
}
mutex_unlock(&adap->bus_lock);
return ret;
} else {
dev_dbg(&adap->dev, "I2C level transfers not supported\n");
return -EOPNOTSUPP;
}
}
我们在注册分析中知道adap->algo被赋值为&mxc_i2c_algorithm,而mxc_i2c_algorithm定义为:
static struct i2c_algorithm mxc_i2c_algorithm = {
.master_xfer = mxc_i2c_xfer,//传输函数
.functionality = mxc_i2c_func//功能支持
};
所以adap->algo->master_xfer实际为mxc_i2c_xfer,这个函数即为i2c控制器的传输函数:
static int mxc_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
int num)
{
mxc_i2c_device *dev = (mxc_i2c_device *) (i2c_get_adapdata(adap));
int i, ret = 0, addr_comp = 0;
volatile unsigned int sr;
int retry = 5, retry_start = 5;
retry:
if (dev->low_power) {
dev_err(&dev->adap.dev, "I2C Device in low power mode\n");
return -EREMOTEIO;
}
if (num < 1) {
return 0;
}
mxc_i2c_module_en(dev, msgs[0].flags);
sr = readw(dev->membase + MXC_I2SR);//读i2c的状态寄存器
/*
* Check bus state
*/
while ((sr & MXC_I2SR_IBB) && retry--) {//检查i2c总线状态是否处于忙
udelay(5);
sr = readw(dev->membase + MXC_I2SR);
}
if ((sr & MXC_I2SR_IBB) && retry < 0) {
mxc_i2c_module_dis(dev);
dev_err(&dev->adap.dev, "Bus busy\n");
return -EREMOTEIO;
}
//gpio_i2c_active(dev->adap.id);
dev->transfer_done = false;
dev->tx_success = false;
for (i = 0; i < num && ret >= 0; i++) {//num是要传输的msg个数
addr_comp = 0;
/*
* Send the slave address and transfer direction in the
* address cycle
*/
if (i == 0) {
/*
* Send a start or repeat start signal
*/
if (mxc_i2c_start(dev, &msgs[0])){//发送开始信号
if(retry_start-- != 0)
goto retry;
return -EREMOTEIO;
}
/* Wait for the address cycle to complete */
if (mxc_i2c_wait_for_tc(dev, msgs[0].flags)) {//等待地址周期完成
mxc_i2c_stop(dev);
//gpio_i2c_inactive(dev->adap.id);
mxc_i2c_module_dis(dev);
return -EREMOTEIO;
}
addr_comp = 1;
} else {
/*
* Generate repeat start only if required i.e the address
* changed or the transfer direction changed
*/
if ((msgs[i].addr != msgs[i - 1].addr) ||
((msgs[i].flags & I2C_M_RD) !=
(msgs[i - 1].flags & I2C_M_RD))) {//从if条件可以看出是判断地址和读写方向
mxc_i2c_repstart(dev, &msgs[i]);
/* Wait for the address cycle to complete */
if (mxc_i2c_wait_for_tc(dev, msgs[i].flags)) {
mxc_i2c_stop(dev);
//gpio_i2c_inactive(dev->adap.id);
mxc_i2c_module_dis(dev);
return -EREMOTEIO;
}
addr_comp = 1;
}
}
/* Transfer the data */
if (msgs[i].flags & I2C_M_RD) {//根据读写标记决定是读操作还是写操作
/* Read the data */
ret = mxc_i2c_readbytes(dev, &msgs[i], (i + 1 == num),
addr_comp);
if (ret < 0) {
dev_err(&dev->adap.dev, "mxc_i2c_readbytes:"
" fail.\n");
break;
}
} else {
/* Write the data */
ret = mxc_i2c_writebytes(dev, &msgs[i], (i + 1 == num));
if (ret < 0) {
dev_err(&dev->adap.dev, "mxc_i2c_writebytes:"
" fail.\n");
break;
}
}
}
//gpio_i2c_inactive(dev->adap.id);
mxc_i2c_module_dis(dev);
/*
* Decrease by 1 as we do not want Start message to be included in
* the count
*/
return (i < 0 ? ret : i);
}
我们看到控制器的传输函数是按照i2c协议的传输过程。在这里我们要看下mxc_i2c_start(dev, &msgs[0]):
static int mxc_i2c_start(mxc_i2c_device *dev, struct i2c_msg *msg)
{
volatile unsigned int cr, sr;
unsigned int addr_trans;
int retry = 16;
retry:
/*
* Set the slave address and the requested transfer mode
* in the data register
*/
addr_trans = msg->addr << 1;//地址x2,所以设备开始设置地址时要设为写地址的1/2
if (msg->flags & I2C_M_RD) {//如果是读操作,这地址加1
addr_trans |= 0x01;
}
......
}
我们注意一下这里的地址转换,所以设备信息设置的时候注意一下地址大小需要除以2 。
上面就是i2c读写过程:driver->i2c_core->adapter
GitHub 加速计划 / li / linux-dash
10.39 K
1.2 K
下载
A beautiful web dashboard for Linux
最近提交(Master分支:2 个月前 )
186a802e
added ecosystem file for PM2 4 年前
5def40a3
Add host customization support for the NodeJS version 4 年前
更多推荐
已为社区贡献2条内容
所有评论(0)