CubieBoard中文论坛

 找回密码
 立即注册
搜索
热搜: unable
查看: 10046|回复: 0

I2C进阶3

[复制链接]
发表于 2014-9-25 11:29:46 | 显示全部楼层 |阅读模式
2.      I2C核心

  (1)增加/删除i2c_adapter

int i2c_add_adapter(struct i2c_adapter *adap);
int i2c_del_adapter(struct i2c_adapter *adap);

(2)增加/删除i2c_driver
int i2c_register_driver(struct module *owner, struct i2c_driver *driver);
int i2c_del_driver(struct i2c_driver *driver);
inline int i2c_add_driver(struct i2c_driver *driver);

(3)i2c_client依附/脱离
int i2c_attach_client(struct i2c_client *client);
int i2c_detach_client(struct i2c_client *client);

(4)i2c传输、发送和接收
int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg *msgs, int num);
int i2c_master_send(struct i2c_client *client,const char *buf ,int count);
int i2c_master_recv(struct i2c_client *client, char *buf ,int count);



3.      I2C总线驱动

3.1硬件部分

        软件部分

struct i2c_adapter {

struct module *owner;

unsigned int class; /* classes to allow probing for */

const struct i2c_algorithm *algo; /* the algorithm to access the bus//指向适配器的驱动程序 */

void *algo_data; //指向适配器的私有数据

/* data fields that are valid for all devices */

struct rt_mutex bus_lock;

int timeout; /* in jiffies //超时时间设置*/

int retries;

struct device dev; /* the adapter device */

int nr;

char name[48];

struct completion dev_released;

struct mutex userspace_clients_lock;

struct list_head userspace_clients;

};

struct i2c_algorithm {

/* If an adapter algorithm can't do I2C-level access, set master_xfer

to NULL. If an adapter algorithm can do SMBus access, set

smbus_xfer. If set to NULL, the SMBus protocol is simulated

using common I2C messages */

/* master_xfer should return the number of messages successfully

processed, or a negative value on error //指向 I2C总线通信协议的函数*/

int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,

int num);

int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,

unsigned short flags, char read_write,

u8 command, int size, union i2c_smbus_data *data);

    //实现SMBUS总线通信协议的函数,一般置为NULL

/* To determine what the adapter supports */

u32 (*functionality) (struct i2c_adapter *); //确定适配器支持的类型

};

http://blog.csdn.net/weiqing1981127/article/details/8010762

struct sunxi_i2c {

int bus_num;

unsigned int status; /* start, running, idle */

unsigned int suspend_flag;

spinlock_t lock; /* syn */

wait_queue_head_t wait;

struct i2c_msg *msg;

unsigned int msg_num;

unsigned int msg_idx;

unsigned int msg_ptr;

struct i2c_adapter adap;

struct clk *clk;

struct clk *pclk;

unsigned int bus_freq;

unsigned int gpio_hdle;

void __iomem *base_addr;

unsigned long iobase; // for remove

unsigned long iosize; // for remove

int irq;

#ifdef CONFIG_CPU_FREQ

struct notifier_block freq_transition;

#endif

unsigned int debug_state; /* log the twi machine state */

};

static const struct i2c_algorithm i2c_sunxi_algorithm = {

.master_xfer = i2c_sunxi_xfer,

.functionality = i2c_sunxi_functionality,

};

我们先看i2c_algorithm中的master_xfer成员,刚才说过, i2c_sunxi_xfer是用来确定适配器支持的类型,用于返回总线支持的协议,具体到代码如下

static unsigned int i2c_sunxi_functionality(struct i2c_adapter *adap)

{

return I2C_FUNC_I2C|I2C_FUNC_10BIT_ADDR|I2C_FUNC_SMBUS_EMUL;

}

好了,接下来我们把重点放在i2c_algorithm中的functionality成员上,i2c_sunxi_functionality 函数用于实现I2C通信协议,将i2c_msg消息传给I2C设备。

static int i2c_sunxi_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)

{

struct sunxi_i2c *i2c = (struct sunxi_i2c *)adap->algo_data;

int ret = AWXX_I2C_FAIL;

int i = 0;

if(i2c->suspend_flag) {

i2c_dbg("[i2c-%d] has already suspend, dev addr:%x!\n", i2c->adap.nr, msgs->addr);

return -ENODEV;

}

for(i = adap->retries; i >= 0; i--) {

ret = i2c_sunxi_do_xfer(i2c, msgs, num); //传输到I2C设备的具体函数

if(ret != AWXX_I2C_RETRY) {

goto out;

}

i2c_dbg("Retrying transmission %d\n", i);

udelay(100);

}

ret = -EREMOTEIO;

out:

return ret;

}

我们可以发现i2c_sunxi_xfer 其实主要就是调用i2c_sunxi_do_xfer 完成具体数据的传输任务。那我们接着看看i2c_sunxi_do_xfer 做了哪些事情?

static int i2c_sunxi_do_xfer(struct sunxi_i2c *i2c, struct i2c_msg *msgs, int num)

{

unsigned long timeout = 0; //传输超时

int ret = AWXX_I2C_FAIL;

//int i = 0, j =0;

aw_twi_soft_reset(i2c->base_addr);

udelay(100);

/* test the bus is free,already protect by the semaphore at DEV layer */

while( TWI_STAT_IDLE != aw_twi_query_irq_status(i2c->base_addr)&&

TWI_STAT_BUS_ERR != aw_twi_query_irq_status(i2c->base_addr) &&

TWI_STAT_ARBLOST_SLAR_ACK != aw_twi_query_irq_status(i2c->base_addr) ) {

i2c_dbg("bus is busy, status = %x\n", aw_twi_query_irq_status(i2c->base_addr));

if (sunxi_is_sun7i() && AWXX_I2C_OK ==

twi_send_clk_9pulse(i2c->base_addr, i2c->bus_num)) {

break;

}

ret = AWXX_I2C_RETRY;

goto out;

}

//i2c_dbg("bus num = %d\n", i2c->adap.nr);

//i2c_dbg("bus name = %s\n", i2c->adap.name);

/* may conflict with xfer_complete */

spin_lock_irq(&i2c->lock);

i2c->msg = msgs;

i2c->msg_num = num;

i2c->msg_ptr = 0;

i2c->msg_idx = 0;

i2c->status = I2C_XFER_START;

aw_twi_enable_irq(i2c->base_addr); /* enable irq */ //启动适配器的中断号,允许适配器发出中断

aw_twi_disable_ack(i2c->base_addr); /* disabe ACK */

aw_twi_set_EFR(i2c->base_addr, 0); /* set the special function register,default:0. 设置特殊功能寄存器,默认:0*/

spin_unlock_irq(&i2c->lock);

/*

for(i =0 ; i < num; i++){

for(j = 0; j < msgs->len; j++){

i2c_dbg("baddr = %x \n",msgs->addr);

i2c_dbg("data = %x \n", msgs->buf[j]);

}

i2c_dbg("\n\n");

}

*/

ret = aw_twi_start(i2c->base_addr);/* START signal,needn't clear int flag 启动适配器的消息传输 */

if(ret == AWXX_I2C_FAIL) {

aw_twi_soft_reset(i2c->base_addr);

aw_twi_disable_irq(i2c->base_addr); /* disable irq */

i2c->status = I2C_XFER_IDLE;

ret = AWXX_I2C_RETRY;

goto out;

}

i2c->status = I2C_XFER_RUNNING;

/* sleep and wait, do the transfer at interrupt handler ,timeout = 5*HZ */

//设置等待队列,直到i2c->msg_num == 0为真或5ms到来才被唤醒

timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, i2c->adap.timeout);

/* return code,if(msg_idx == num) succeed */

ret = i2c->msg_idx;

if (timeout == 0){

//dev_dbg(i2c->adap.dev, "timeout \n");

pr_warning("i2c-%d, xfer timeout\n", i2c->bus_num);

ret = -ETIME;

}

else if (ret != num){

printk("incomplete xfer (0x%x)\n", ret);

ret = -ECOMM;

//dev_dbg(i2c->adap.dev, "incomplete xfer (%d)\n", ret);

}

out:

return ret;

}

好了,让我们总结总结,我们可以发现i2c_sunxi_do_xfer主要做了如下几件事:第一,设置为中断传输方式。第二,发送启动信号,传输第一个字节。第三,等待超时或者其他函数在i2c->msg_num == 0时唤醒这里的等待队列。




回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|粤ICP备13051116号|cubie.cc---深刻的嵌入式技术讨论社区

GMT+8, 2024-3-29 15:26 , Processed in 0.022753 second(s), 15 queries .

Powered by Discuz! X3.4

© 2001-2012 Comsenz Inc. | Style by Coxxs

返回顶部