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时唤醒这里的等待队列。
|