您的位置:首页 > 运维架构 > Linux

我对linux理解之i2c 二

2012-10-27 13:55 274 查看
转载出处:amingriyue.blog.chinaunix.net

我们下面开始分析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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: