在i2c_sunxi_core_process这个函数中,我们发现有很多
error_out → aw_twi_stop(base_addr)终止函数,
msg_null --》i2c_sunxi_xfer_complete(i2c, err_code)调用了s3c24xx_i2c_master_complete这个函数来唤醒传输等待队列中的进程
那么让我们来看看这两个函数
static int aw_twi_stop(void *base_addr)
{
unsigned int timeout = 0xff;
aw_twi_set_stop(base_addr); //设置适配器状态为停止
aw_twi_clear_irq_flag(base_addr); //清中断
aw_twi_get_stop(base_addr);/* it must delay 1 nop to check stop bit */
while(( 1 == aw_twi_get_stop(base_addr))&& (--timeout));
if(timeout == 0) {
i2c_dbg("1.STOP can't sendout!\n");
return AWXX_I2C_FAIL;
}
if (sunxi_is_sun7i())
timeout = 0xffff;
else
timeout = 0xff;
while((TWI_STAT_IDLE != readl(base_addr + TWI_STAT_REG))&&(--timeout));
if(timeout == 0)
{
i2c_dbg("i2c state isn't idle(0xf8)\n");
return AWXX_I2C_FAIL;
}
timeout = 0xff;
while((TWI_LCR_IDLE_STATUS != readl(base_addr + TWI_LCR_REG))&&(--timeout));
if(timeout == 0) {
i2c_dbg("2.STOP can't sendout!\n");
return AWXX_I2C_FAIL;
}
return AWXX_I2C_OK;
}
static int i2c_sunxi_xfer_complete(struct sunxi_i2c *i2c, int code)
{
int ret = AWXX_I2C_OK;
i2c->msg = NULL; //表示没有可传输的消息
i2c->msg_num = 0; //表示没有可传输的消息
i2c->msg_ptr = 0; //下一条消息字符串的首地址置0
i2c->status = I2C_XFER_IDLE;
/* i2c->msg_idx store the information */
if(code == AWXX_I2C_FAIL) {
i2c_dbg("Maybe Logic Error,debug it!\n");
i2c->msg_idx = code;
ret = AWXX_I2C_FAIL;
}
else if(code != AWXX_I2C_OK) {
//return the ERROR code, for debug or detect error type
i2c->msg_idx = code; //记录已经传输完的信息个数
ret = AWXX_I2C_FAIL;
}
wake_up(&i2c->wait); //唤醒等待队列中的进程
return ret;
}
此为止,我们已经完成了在I2C总线驱动层填充了i2c_adapter和i2c_algorithm结构体,剩下来我们需要把这两个结构体外包一下,来注册这个适配器,这怎么实现呢?当然我们在上面已经分析了中断处理函数i2c_sunxi_handler,那么这个函数什么时候被注册的呢?带着这两个问题我们需要继续往下走,go
恩恩,有一定平台驱动基础的朋友应该就明白了,平台驱动的这个探测函数i2c_sunxi_probe应该就完成了整个适配器的注册和中断处理函数的注册工作了。别发呆了,那我们就来看看这个si2c_sunxi_probe函数吧。
static int i2c_sunxi_probe(struct platform_device *dev)
{
struct sunxi_i2c *i2c = NULL; //适配器指针
struct resource *res = NULL; //指向资源
struct sunxi_i2c_platform_data *pdata = NULL; //平台数据
char *i2c_clk[] ={"twi0","twi1","twi2"};
char *i2c_pclk[] ={"apb_twi0","apb_twi1","apb_twi2"};
int ret;
int irq;
pdata = dev->dev.platform_data; //获取平台数据
if(pdata == NULL) {
return -ENODEV;
}
res = platform_get_resource(dev, IORESOURCE_MEM, 0);
irq = platform_get_irq(dev, 0);
if (res == NULL || irq < 0) {
return -ENODEV;
}
if (!request_mem_region(res->start, resource_size(res), res->name)) {
return -ENOMEM;
}
i2c = kzalloc(sizeof(struct sunxi_i2c), GFP_KERNEL); //分配适配器空间
if (!i2c) {
ret = -ENOMEM;
goto emalloc;
}
//给适配器赋予名字sunxi-i2c,这个名字会由cat /sys/class/i2c_dev/0/name看到。
strlcpy(i2c->adap.name, "sunxi-i2c", sizeof(i2c->adap.name));
i2c->adap.owner = THIS_MODULE;
i2c->adap.nr = pdata->bus_num;
i2c->adap.retries = 2; //两次总线仲裁尝试
i2c->adap.timeout = 5*HZ;
i2c->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
i2c->bus_freq = pdata->frequency;
i2c->irq = irq;
i2c->bus_num = pdata->bus_num;
i2c->status = I2C_XFER_IDLE;
i2c->suspend_flag = 0;
spin_lock_init(&i2c->lock);
init_waitqueue_head(&i2c->wait);
i2c->pclk = clk_get(NULL, i2c_pclk[i2c->adap.nr]);
if(NULL == i2c->pclk){
i2c_dbg("request apb_i2c clock failed\n");
ret = -EIO;
goto eremap;
}
i2c->clk = clk_get(NULL, i2c_clk[i2c->adap.nr]);
if(NULL == i2c->clk){
i2c_dbg("request i2c clock failed\n");
clk_put(i2c->pclk);
ret = -EIO;
goto eremap;
}
snprintf(i2c->adap.name, sizeof(i2c->adap.name),\
"sunxi-i2c.%u", i2c->adap.nr);
i2c->base_addr = ioremap(res->start, resource_size(res));
if (!i2c->base_addr) {
ret = -EIO;
goto eremap;
}
#ifndef SYS_I2C_PIN
gpio_addr = ioremap(_PIO_BASE_ADDRESS, 0x1000);
if(!gpio_addr) {
ret = -EIO;
goto ereqirq;
}
#endif
i2c->adap.algo = &i2c_sunxi_algorithm;
ret = request_irq(irq, i2c_sunxi_handler, IRQF_DISABLED, i2c->adap.name, i2c);
if (ret)
{
goto ereqirq;
}
if(-1 == i2c_sunxi_hw_init(i2c)){
ret = -EIO;
goto eadapt;
}
i2c->adap.algo_data = i2c;
i2c->adap.dev.parent = &dev->dev;
i2c->iobase = res->start;
i2c->iosize = resource_size(res);
ret = i2c_add_numbered_adapter(&i2c->adap);
if (ret < 0) {
i2c_dbg(KERN_INFO "I2C: Failed to add bus\n");
goto eadapt;
}
platform_set_drvdata(dev, i2c);
i2c_dbg(KERN_INFO "I2C: %s: AW16XX I2C adapter\n",
dev_name(&i2c->adap.dev));
return 0;
eadapt:
free_irq(irq, i2c);
clk_disable(i2c->clk);
ereqirq:
#ifndef SYS_I2C_PIN
if(gpio_addr){
iounmap(gpio_addr);
}
#endif
iounmap(i2c->base_addr);
clk_put(i2c->clk);
eremap:
kfree(i2c);
emalloc:
release_mem_region(i2c->iobase, i2c->iosize);
return ret;
}
|