i2c适配器驱动源码分析(i2c总线驱动)

i2c适配器源码位置在driver/i2c/buss下这里以i2c-sunxi.c为例,i2c适配器设备和驱动的加载绑定过程也可以看做是i2c总线驱动的加载过程,I2C总线驱动是对I2C硬件体系结构中适配器端的实现,适配器可由CPU控制,甚至可以直接集成在CPU内部。
下面以源码进行分析:
函数的入口:

subsys_initcall(sunxi_i2c_adap_init);  //和一般的驱动函数一样存在入口函数

static int __init sunxi_i2c_adap_init(void)//入口函数
{
    I2C_DBG("Sunxi I2C adapt init\n");

    return platform_driver_register(&sunxi_i2c_driver);//向platform总线注册这个驱动
}

定义适配器驱动:

static struct platform_driver sunxi_i2c_driver = {
    .probe      = sunxi_i2c_probe,//定义probe函数,设备和驱动绑定后
                                     第一个执行的函数
    .remove     = sunxi_i2c_remove,
    .driver     = {
        .name   = SUNXI_TWI_DEV_NAME, //该驱动的名称
        .owner  = THIS_MODULE,
        .pm     = SUNXI_I2C_DEV_PM_OPS,
        .of_match_table = sunxi_i2c_match, //定义匹配函数,这个函数
                                             很重要,内核会依据这个
                                             函数中compatible的值
                                             是否相等来决定是否绑定
    },
};
static const struct of_device_id sunxi_i2c_match[] = {
    { .compatible = "allwinner,sun8i-twi", },
    { .compatible = "allwinner,sun50i-twi", },
    { .compatible = "allwinner,sun3i-twi", },
    {},//内核会自动将这里的值和设备树中的compatible的值进行匹配,匹配成功则将设备和驱动绑定并执行probe函数
};

probe函数:
主要完成adapter的注册和向i2c总线添加adapter工作

static int sunxi_i2c_probe(struct platform_device *pdev)
{
    struct device_node *np = pdev->dev.of_node; 
    struct sunxi_i2c *i2c = NULL;
    struct resource *mem_res = NULL;
    struct sunxi_i2c_platform_data *pdata = NULL;
    int ret, irq;
    unsigned long int_flag = IRQF_DISABLED;

    if (np == NULL) {
        I2C_ERR("I2C failed to get of node\n");//获取设备的节点
        return -ENODEV;
    }

    i2c = kzalloc(sizeof(struct sunxi_i2c), GFP_KERNEL);
    if (!i2c) {
        return -ENOMEM;
    }

    pdata = kzalloc(sizeof(struct sunxi_i2c_platform_data), GFP_KERNEL);
    if (pdata == NULL) {
        kfree(i2c);
        return -ENOMEM;
    }
    pdev->dev.platform_data = pdata;

    pdev->id = of_alias_get_id(np, "twi");
    if (pdev->id < 0) {
        I2C_ERR("I2C failed to get alias id\n");
        ret = -EINVAL;
        goto emem;
    }
    pdata->bus_num  = pdev->id;

    mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    if (mem_res == NULL) {
        I2C_ERR("[I2C%d] failed to get MEM res\n", pdev->id);
        ret = -ENXIO;
        goto emem;
    }

    if (!request_mem_region(mem_res->start, resource_size(mem_res), mem_res->name)) {
        I2C_ERR("[I2C%d] failed to request mem region\n", pdev->id);
        ret = -EINVAL;
        goto emem;
    }

    i2c->base_addr = ioremap(mem_res->start, resource_size(mem_res));
    if (!i2c->base_addr) {
        ret = -EIO;
        goto eiomap;
    }

    irq = platform_get_irq(pdev, 0);
    if (irq < 0) {
        I2C_ERR("[I2C%d] failed to get irq\n", pdev->id);
        ret = -EINVAL;
        goto eiomap;
    }

    ret = of_property_read_u32(np, "clock-frequency", &pdata->frequency); //获取时钟频率
    if (ret) {
        I2C_ERR("[I2C%d] failed to get clock frequency\n", pdev->id);
        ret = -EINVAL;
        goto eiomap;
    }

    pdev->dev.release = sunxi_i2c_release;
    i2c->adap.owner   = THIS_MODULE;
    i2c->adap.nr      = pdata->bus_num;  //i2c号,使用第几组i2c总线
    i2c->adap.retries = 3;   //第一次和i2c设备的同学次数
    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->suspended = 0; 
    snprintf(i2c->adap.name, sizeof(i2c->adap.name), SUNXI_TWI_DEV_NAME"%u", i2c->adap.nr);
    pdev->dev.init_name = i2c->adap.name;

    spin_lock_init(&i2c->lock);
    init_waitqueue_head(&i2c->wait);

    i2c->mclk = of_clk_get(np, 0);
    if (IS_ERR_OR_NULL(i2c->mclk)) {
        I2C_ERR("[i2c%d] request TWI clock failed\n", i2c->bus_num);
        ret = -EIO;
        goto eremap;
    }

    i2c->adap.algo = &sunxi_i2c_algorithm;

#ifndef CONFIG_SUNXI_ARISC
    /* SUNXI_ARISC will only use twi0, enable gic interrupt when suspend */
    if (0 == i2c->adap.nr)
        int_flag |= IRQF_NO_SUSPEND;
#endif

    ret = request_irq(irq, sunxi_i2c_handler, int_flag, i2c->adap.name, i2c);
    if (ret) {
        I2C_ERR("[i2c%d] requeset irq failed!\n", i2c->bus_num);
        goto ereqirq;
    }

    i2c->adap.algo_data  = i2c;
    i2c->adap.dev.parent = &pdev->dev;
    i2c->adap.dev.of_node = pdev->dev.of_node;
    twi_used_mask |= SUNXI_TWI_CHAN_MASK(pdev->id);

    if (sunxi_i2c_hw_init(i2c, pdata)) {
        ret = -EIO;
        goto ehwinit;
    }

    ret = i2c_add_numbered_adapter(&i2c->adap);//向i2c总线添加adapter
    if (ret < 0) {
        I2C_ERR( "[i2c%d] failed to add adapter\n", i2c->bus_num);
        goto eadapt;
    }

    of_i2c_register_devices(&i2c->adap); //注册这个驱动对应的设备
    platform_set_drvdata(pdev, i2c);

    sunxi_i2c_sysfs(pdev);

    I2C_DBG("I2C: %s: sunxi I2C adapter\n", dev_name(&i2c->adap.dev));
    I2C_DBG("TWI_CTL  0x%p: 0x%08x \n", i2c->base_addr + 0x0c, readl(i2c->base_addr + 0x0c));
    I2C_DBG("TWI_STAT 0x%p: 0x%08x \n", i2c->base_addr + 0x10, readl(i2c->base_addr + 0x10));
    I2C_DBG("TWI_CLK  0x%p: 0x%08x \n", i2c->base_addr + 0x14, readl(i2c->base_addr + 0x14));
    I2C_DBG("TWI_SRST 0x%p: 0x%08x \n", i2c->base_addr + 0x18, readl(i2c->base_addr + 0x18));
    I2C_DBG("TWI_EFR  0x%p: 0x%08x \n", i2c->base_addr + 0x1c, readl(i2c->base_addr + 0x1c));

    return 0;

eadapt:
    clk_disable_unprepare(i2c->mclk);

ehwinit:
    free_irq(irq, i2c);

ereqirq:
    iounmap(i2c->base_addr);

eremap:
    if (!IS_ERR_OR_NULL(i2c->mclk)) {
        clk_put(i2c->mclk);
        i2c->mclk = NULL;
    }

eiomap:
    release_mem_region(mem_res->start, resource_size(mem_res));

emem:
    kfree(pdata);
    kfree(i2c);

    return ret;
}

注:这个probe函数只有在设备和驱动绑定成功的时候才会被执行,实际调试时可以在在此函数中打debug,来判断probe函数是否被执行。从而判断设备树的信息和驱动中的compatible的值是否配置正确;

在适配器中还有一个比较重要的函数,是用来和i2c设备进行通讯:
在i2c设备和适配器进行通讯时,i2c设备的驱动会调用i2c_transfer函数,通过i2c_transfer函数调用algorithm函数,algorithm函数实际就是对master_xfer的调用,而master_xfer函数在适配器被定义为sunxi_i2c_xfer,也就是i2c设备通过调用i2c_transfer,然后一层一层的调用,最终调用了是适配器的sunxi_i2c_xfer函数来完成和i2c设备的通讯;(sunxi_i2c_xfer会调用sunxi_i2c_do_xfer来完成收发通讯)在对i2c设备进行调试的时候要熟悉这个调用流程,才知道问题所在;
下面一层一层的看源码:

i2c->adap.algo = &sunxi_i2c_algorithm;

adap是一个i2c_adapter结构体

定义了master_xfer和sunxi_i2c_xfer的对应关系

static const struct i2c_algorithm sunxi_i2c_algorithm = {
    .master_xfer      = sunxi_i2c_xfer,
    .functionality    = sunxi_i2c_functionality,
};

调用sunxi_i2c_xfer来实现通讯,其内部是最终通过调用sunxi_i2c_do_xfer函数

static int sunxi_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
    struct sunxi_i2c *i2c = (struct sunxi_i2c *)adap->algo_data;
    int ret = SUNXI_I2C_FAIL;
    int i   = 0;

    if (i2c->suspended) {
        I2C_ERR("[i2c%d] has already suspend, dev addr:0x%x!\n", i2c->adap.nr, msgs->addr);
        return -ENODEV;
    }

    for (i = 1; i <= adap->retries; i++) {
        ret = sunxi_i2c_do_xfer(i2c, msgs, num); //最终是通过调用此函数进行通讯 

        if (ret != SUNXI_I2C_RETRY) {
            goto out;
        }

        I2C_DBG("[i2c%d] Retrying transmission %d\n", i2c->adap.nr, i);
        udelay(100);
    }

    ret = -EREMOTEIO;
out:
    return ret;
}

sunxi_i2c_xfer 调用 sunxi_i2c_do_xfer函数

static int sunxi_i2c_do_xfer(struct sunxi_i2c *i2c, struct i2c_msg *msgs, int num)
{
    unsigned long timeout = 0;
    int ret = SUNXI_I2C_FAIL;
    unsigned long flags = 0;
    //int i = 0, j =0;

    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 != twi_query_irq_status(i2c->base_addr)&&
           TWI_STAT_BUS_ERR != twi_query_irq_status(i2c->base_addr) &&
           TWI_STAT_ARBLOST_SLAR_ACK != twi_query_irq_status(i2c->base_addr)) {
        I2C_DBG("[i2c%d] bus is busy, status = %x\n", i2c->bus_num, twi_query_irq_status(i2c->base_addr));
        if (SUNXI_I2C_OK == twi_send_clk_9pulse(i2c->base_addr, i2c->bus_num)) {
            break;
        } else {
            ret = SUNXI_I2C_RETRY;
            goto out;
        }
    }

    /* may conflict with xfer_complete */
    spin_lock_irqsave(&i2c->lock, flags);
    i2c->msg     = msgs;
    i2c->msg_num = num;
    i2c->msg_ptr = 0;
    i2c->msg_idx = 0;
    i2c->status  = I2C_XFER_START;
    twi_enable_irq(i2c->base_addr);  /* enable irq */
    twi_disable_ack(i2c->base_addr); /* disabe ACK */
    twi_set_efr(i2c->base_addr, 0);  /* set the special function register,default:0. */
    spin_unlock_irqrestore(&i2c->lock, flags);

    /* START signal, needn't clear int flag */
    ret = twi_start(i2c->base_addr, i2c->bus_num);//开始start,开始传输
    if (ret == SUNXI_I2C_FAIL) {
        twi_soft_reset(i2c->base_addr);
        twi_disable_irq(i2c->base_addr);  /* disable irq */
        i2c->status  = I2C_XFER_IDLE;
        ret = SUNXI_I2C_RETRY;
        goto out;
    }

    i2c->status  = I2C_XFER_RUNNING;
    /* sleep and wait, do the transfer at interrupt handler ,timeout = 5*HZ */
    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) {
        I2C_ERR("[i2c%d] xfer timeout (dev addr:0x%x)\n", i2c->bus_num, msgs->addr);
        spin_lock_irqsave(&i2c->lock, flags);
        i2c->msg = NULL;
        spin_unlock_irqrestore(&i2c->lock, flags);
        ret = -ETIME;
    } else if (ret != num) {
        I2C_ERR("[i2c%d] incomplete xfer (status: 0x%x, dev addr: 0x%x)\n", i2c->bus_num, ret, msgs->addr);
        ret = -ECOMM;
    }
out:
    return ret;
}

该驱动的源码主要是ada和acl的gpio口的注册等等,在调试的时候一定注意通讯时函数的调用流程,这也是此类驱动的一个框架;

下面给出i2c_msg的结构
这里写图片描述

猜你喜欢

转载自blog.csdn.net/chihunqi5879/article/details/78311423