到此为止我们会带来几个疑问:第一, aw_twi_enable_irq和 aw_twi_start具体怎么实现的?第二,等待队列在何时被唤醒呢?
首先我们先来研究第一个问题,aw_twi_enable_irq实现开中断如下:
static inline void aw_twi_enable_irq(void *base_addr)
{
unsigned int reg_val = readl(base_addr + TWI_CTL_REG);
/*
* 1 when enable irq for next operation, set intflag to 1 to prevent to clear it by a mistake
* (intflag bit is write-0-to-clear bit)
* 2 Similarly, mask startbit and stopbit to prevent to set it twice by a mistake
* (start bit and stop bit are self-clear-to-0 bits)
*/
reg_val |= (TWI_CTL_INTEN | TWI_CTL_INTFLG);
reg_val &= ~(TWI_CTL_STA | TWI_CTL_STP);
writel(reg_val, base_addr + TWI_CTL_REG);
}
/* function */
static int aw_twi_start(void *base_addr)
{
unsigned int timeout = 0xff;
aw_twi_set_start(base_addr); /* trigger start signal, the start bit will be cleared automatically */
while((1 == aw_twi_get_start(base_addr))&&(--timeout));
/* get start bit status, poll if start signal is sent */
if(timeout == 0) {
i2c_dbg("START can't sendout!\n");
return AWXX_I2C_FAIL;
}
return AWXX_I2C_OK;
}
总结下我们这个 aw_twi_start函数吧,这个函数主要做了两件事,第一使能ACK信号。第二,将从机地址和读写方式控制字写入待IICDS中,由IICSTAT发送S信号,启动发送从机地址。
嗯,到现在为止我们已经把前面提出的第一个问题解决了,该轮到解决第二个问题了,s3c24xx_i2c_doxfer中的等待队列何时被唤醒呢?其实分析到现在我们发现s3c24xx_i2c_doxfer调用3c24xx_i2c_message_start只是发送了一个从机的地址。真正的数据传输在哪里呢?其实真正的数据传输我们放在了中断处理函数中实现的。当数据准备好发送时,将产生中断,并调用事先注册的中断处理函数s3c24xx_i2c_irq进行数据传输。中断的产生其实有3种情况,第一,总线仲裁失败。第二,当总线还处于空闲状态,因为一些错误操作等原因,意外进入了中断处理函数。第三,收发完一个字节的数据,或者当收发到的I2C设备地址信息吻合。行,那我们先来看看s3c24xx_i2c_irq到底怎么来传输数据的吧。
static irqreturn_t i2c_sunxi_handler(int this_irq, void * dev_id)
{
struct sunxi_i2c *i2c = (struct sunxi_i2c *)dev_id;
int ret = AWXX_I2C_FAIL;
if(!aw_twi_query_irq_flag(i2c->base_addr)) { //获取中断标志位,成功返回标志位
pr_warning("unknown interrupt!");
return ret;
//return IRQ_HANDLED;
}
/* disable irq */
aw_twi_disable_irq(i2c->base_addr);
//twi core process
ret = i2c_sunxi_core_process(i2c);
/* enable irq only when twi is transfering, otherwise,disable irq 当twi传输数据时候进入中断,否则。关闭终端*/
if(i2c->status != I2C_XFER_IDLE) {
aw_twi_enable_irq(i2c->base_addr);
}
return IRQ_HANDLED;
}
我们发现其实中断处理函数s3c24xx_i2c_irq中真正传输数据的函数是 i2s_s3c_irq_nextbyte。走了这么久,其实i2s_s3c_irq_nextbyte才是真正的传输数据的核心函数,那我们赶紧来看看 i2s_s3c_irq_nextbyte吧。
ret = i2c_sunxi_core_process(i2c);
static int i2c_sunxi_core_process(struct sunxi_i2c *i2c)
{
void *base_addr = i2c->base_addr;
int ret = AWXX_I2C_OK;
int err_code = 0;
unsigned char state = 0;
unsigned char tmp = 0;
state = aw_twi_query_irq_status(base_addr);
#ifdef CONFIG_SUNXI_IIC_PRINT_TRANSFER_INFO
if(i2c->bus_num == CONFIG_SUNXI_IIC_PRINT_TRANSFER_INFO_WITH_BUS_NUM){
i2c_dbg("sunxi_i2c->bus_num = %d, sunxi_i2c->msg->addr = (0x%x) state = (0x%x)\n", \
i2c->bus_num, i2c->msg->addr, state);
}
#endif
if(i2c->msg == NULL) {
printk("i2c->msg is NULL, err_code = 0xfe\n");
err_code = 0xfe;
goto msg_null;
}
switch(state) {
case 0xf8: /* On reset or stop the bus is idle, use only at poll method */
err_code = 0xf8;
goto err_out;
case 0x08: /* A START condition has been transmitted */ 发送开始信号S
case 0x10: /* A repeated start condition has been transmitted */
i2c_sunxi_addr_byte(i2c);/* send slave address */
break;
//当没有接收到ACK应答信号,说明I2C设备不存在,应停止总线工作
case 0xd8: /* second addr has transmitted, ACK not received! */
case 0x20: /* SLA+W has been transmitted; NOT ACK has been received */
err_code = 0x20;
goto err_out;
/* 0x18 Address+Write bit transmitted, ACK received */
case 0x18: /* SLA+W has been transmitted; ACK has been received */
/* if any, send second part of 10 bits addr */
if(i2c->msg[i2c->msg_idx].flags & I2C_M_TEN) {
tmp = i2c->msg[i2c->msg_idx].addr & 0xff; /* the remaining 8 bits of address */
aw_twi_put_byte(base_addr, &tmp); /* case 0xd0: */
break;
}
/* for 7 bit addr, then directly send data byte--case 0xd0: */
case 0xd0: /* second addr has transmitted,ACK received! */
case 0x28: /* Data byte in DATA REG has been transmitted; ACK has been received */
/* after send register address then START send write data 。。。开始发送写数据*/
if(i2c->msg_ptr < i2c->msg[i2c->msg_idx].len) {
aw_twi_put_byte(base_addr, &(i2c->msg[i2c->msg_idx].buf[i2c->msg_ptr]));
i2c->msg_ptr++;
break;
}
i2c->msg_idx++; /* the other msg */ //表示已经传输完1条消息
i2c->msg_ptr = 0; //下一条消息字符串的首地址置0
if (i2c->msg_idx == i2c->msg_num) {
err_code = AWXX_I2C_OK;/* Success,wakeup */
goto ok_out;
}
else if(i2c->msg_idx < i2c->msg_num) {/* for restart pattern */
ret = aw_twi_restart(base_addr);/* read spec, two msgs */
if(ret == AWXX_I2C_FAIL) {
err_code = AWXX_I2C_SFAIL;
goto err_out;/* START can't sendout */
}
}
else {
err_code = AWXX_I2C_FAIL;
goto err_out;
}
break;
case 0x30: /* Data byte in I2CDAT has been transmitted; NOT ACK has been received */
err_code = 0x30;//err,wakeup the thread
goto err_out;
case 0x38: /* Arbitration lost during SLA+W, SLA+R or data bytes */
err_code = 0x38;//err,wakeup the thread
goto err_out;
case 0x40: /* SLA+R has been transmitted; ACK has been received */
/* with Restart,needn't to send second part of 10 bits addr,refer-"I2C-SPEC v2.1" */
/* enable A_ACK need it(receive data len) more than 1. */
if(i2c->msg[i2c->msg_idx].len > 1) {
/* send register addr complete,then enable the A_ACK and get ready for receiving data */
aw_twi_enable_ack(base_addr);
aw_twi_clear_irq_flag(base_addr);/* jump to case 0x50 */
}
else if(i2c->msg[i2c->msg_idx].len == 1) {
aw_twi_clear_irq_flag(base_addr);/* jump to case 0x58 */
}
break;
case 0x48: /* SLA+R has been transmitted; NOT ACK has been received */
err_code = 0x48;//err,wakeup the thread
goto err_out;
// t调用aw_twi_get_byte读取数据。从读取数据,将读取到的数据放入缓存区, msg_ptr++直到当前消息传输完毕
case 0x50: /* Data bytes has been received; ACK has been transmitted 数据位以及接收到,ACK没有传送到 */
/* receive first data byte */
if (i2c->msg_ptr < i2c->msg[i2c->msg_idx].len) {
/* more than 2 bytes, the last byte need not to send ACK */
if( (i2c->msg_ptr + 2) == i2c->msg[i2c->msg_idx].len ) {
aw_twi_disable_ack(base_addr);/* last byte no ACK */
}
/* get data then clear flag,then next data comming */
aw_twi_get_byte(base_addr, &i2c->msg[i2c->msg_idx].buf[i2c->msg_ptr]);
i2c->msg_ptr++;
break;
}
/* err process, the last byte should be @case 0x58 */
err_code = AWXX_I2C_FAIL;/* err, wakeup */
goto err_out;
case 0x58: /* Data byte has been received; NOT ACK has been transmitted 数据位读到了,没有ACK被传送到*/
/* received the last byte */
//判断是当前处理的消息是否是最后一个消息
if ( i2c->msg_ptr == i2c->msg[i2c->msg_idx].len - 1 ) {
aw_twi_get_last_byte(base_addr, &i2c->msg[i2c->msg_idx].buf[i2c->msg_ptr]);
i2c->msg_idx++;
i2c->msg_ptr = 0;
if (i2c->msg_idx == i2c->msg_num) {
err_code = AWXX_I2C_OK; // succeed,wakeup the thread
goto ok_out;
}
else if(i2c->msg_idx < i2c->msg_num) {
/* repeat start */
ret = aw_twi_restart(base_addr);
if(ret == AWXX_I2C_FAIL) {/* START fail */
err_code = AWXX_I2C_SFAIL;
goto err_out;
}
break;
}
}
else {
err_code = 0x58;
goto err_out;
}
case 0x00: /* Bus error during master or slave mode due to illegal level condition */
err_code = 0xff;
goto err_out;
default:
err_code = state;
goto err_out;
}
i2c->debug_state = state;/* just for debug */
return ret;
ok_out:
err_out:
if(AWXX_I2C_FAIL == aw_twi_stop(base_addr)) {
i2c_dbg("STOP failed!\n");
}
msg_null:
ret = i2c_sunxi_xfer_complete(i2c, err_code);/* wake up */
i2c->debug_state = state;/* just for debug */
return ret;
}
我们终于把这个庞大的搞定了,在这里需要说明几点,第一,消息分为第一条消息,第二条消息,第三条消息等,总共有msg_num条消息,每发送完一个消息,会msg_idx++。每条消息发送完还需要调用
aw_twi_restart进行发送新的起始信号S。第二,第i条消息是一个字符串,按照一个字节一个字节的形式发送,由一个指针msg_ptr指向这个字符串的待发送字节的地址。
|