Kernel的IIC驱动分析

Posted maogefff

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Kernel的IIC驱动分析相关的知识,希望对你有一定的参考价值。

涉及到的文件:

drivers/i2c/i2c-core.c

drivers/i2c/i2c-dev.c

drivers/i2c/busses/i2c-imx.c

等等

 

在下面分析的代码中,不想关或者不重要的,我会省略掉。

1.  适配器设备的注册

在Linux内核启动的过程中,会调用到mx6_sabresd_board_init函数

static void __init mx6_sabresd_board_init(void)

{

    。。。省略。。。

/*在这里修改了i2c0适配器上设备的类型,同时添加了平台数据,后面会分析到*/ 

    strcpy(mxc_i2c0_board_info[0].type, "wm8962");

    mxc_i2c0_board_info[0].platform_data = &wm8962_config_data;

    /*第一步:注册适配器设备:详见下面分析*/

    imx6q_add_imx_i2c(0, &mx6q_sabresd_i2c_data);

    /*第二步:添加I2C从设备*/   

i2c_register_board_info(0, mxc_i2c0_board_info,

            ARRAY_SIZE(mxc_i2c0_board_info));

    。。。省略。。。

}

 

下面我们来分析第一步:注册适配器设备

extern const struct imx_imx_i2c_data imx6q_imx_i2c_data[] __initconst;

#define imx6q_add_imx_i2c(id, pdata)    \

    imx_add_imx_i2c(&imx6q_imx_i2c_data[id], pdata)

 

接着追到imx_add_imx_i2c,我们可以知道

1). 通过imx_add_platform_device ,我们知道I2C的适配器设备是放入了平台总线模型当中,并且名字为imx-i2c。

2). 第一个参数imx_imx_i2c_data是适配器的设备信息,包括I2C寄存器地址,中断号,资源res是通过data的重新赋值。它包括了I2C寄存器的基地址,中断号,总线编号。第二个参数imxi2c_platform_data是存放在平台上的数据,下面我们来分析这两个参数。

struct platform_device *__init imx_add_imx_i2c(

        const struct imx_imx_i2c_data *data,

        const struct imxi2c_platform_data *pdata)

{

    struct resource res[] = {

        {

            .start = data->iobase,

            .end = data->iobase + data->iosize - 1,

            .flags = IORESOURCE_MEM,

        }, {

            .start = data->irq,

            .end = data->irq,

            .flags = IORESOURCE_IRQ,

        },

    };

 

    return imx_add_platform_device("imx-i2c", data->id,

            res, ARRAY_SIZE(res),

            pdata, sizeof(*pdata));

}

 

我们先分析第一个参数imx_add_platform_device:

我们往回查看上述函数的关键数据

imx_imx_i2c_data* data= imx6q_imx_i2c_data[0]

const struct imx_imx_i2c_data imx6q_imx_i2c_data[] __initconst = {

#define imx6q_imx_i2c_data_entry(_id, _hwid)                \

    imx_imx_i2c_data_entry(MX6Q, _id, _hwid, SZ_4K)

    imx6q_imx_i2c_data_entry(0, 1),

    imx6q_imx_i2c_data_entry(1, 2),

    imx6q_imx_i2c_data_entry(2, 3),

 

};

 

接着追imx_imx_i2c_data_entry

#define imx_imx_i2c_data_entry(soc, _id, _hwid, _size)          \

    [_id] = imx_imx_i2c_data_entry_single(soc, _id, _hwid, _size)

 

再接着追imx_imx_i2c_data_entry

#define imx_imx_i2c_data_entry_single(soc, _id, _hwid, _size)       \

    {                               \

        .id = _id,                      \

        .iobase = soc ## _I2C ## _hwid ## _BASE_ADDR,       \

        .iosize = _size,                    \

        .irq = soc ## _INT_I2C ## _hwid,            \

    }

 

通过一层层的代入展开,可以推导出:

imx6q_imx_i2c_data[0]=

{                                                      

              .id = 0,                                      

              .iobase = MX6Q_I2C1_BASE_ADDR,         

              .iosize = _SZ_4K,                              

              .irq = MX6Q_INT_I2C1,              

}

 

第二个参数mx6q_sabresd_i2c_data是时钟系数:

static struct imxi2c_platform_data mx6q_sabresd_i2c_data = {

    .bitrate = 100000,

};

 

2.  适配器驱动的流程

从之前的代码,我们得知适配器设备在平台驱动模型中的名字为imx-i2c。

其代码在drivers/i2c/busses/i2c-imx.c

static struct platform_driver i2c_imx_driver = {

    .remove     = __exit_p(i2c_imx_remove),

    .driver = {

        .name   = DRIVER_NAME,   /*imx-i2c*/

        .owner  = THIS_MODULE,

    }

};

/*通过平台模型注册*/

static int __init i2c_adap_imx_init(void)

{

return platform_driver_probe(&i2c_imx_driver, i2c_imx_probe);

}

 

当名字imx-i2c匹配上的时候,会自动调用探测函数i2c_imx_probe,其原理属于平台驱动模型。在这里面会对I2C进行初始化:设置始终,初始化I2C寄存器,设置中断等。

static int __init i2c_imx_probe(struct platform_device *pdev)

{

    struct imx_i2c_struct *i2c_imx;

    struct resource *res;

    struct imxi2c_platform_data *pdata;

    void __iomem *base;

    resource_size_t res_size;

    int irq;

    int ret;

 

    dev_dbg(&pdev->dev, "<%s>\n", __func__);

    /*获取来自适配器设备上的内存资源:I2C寄存器地址*/

    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);

    if (!res) {

        dev_err(&pdev->dev, "can‘t get device resources\n");

        return -ENOENT;

}

/*获取来自适配器设备上的中断号*/

    irq = platform_get_irq(pdev, 0);

    if (irq < 0) {

        dev_err(&pdev->dev, "can‘t get irq number\n");

        return -ENOENT;

    }

    /*获取平台数据:也就是之前分析的时钟系数*/

    pdata = pdev->dev.platform_data;

   

    if (pdata && pdata->init) {

        ret = pdata->init(&pdev->dev);

        if (ret)

            return ret;

    }

   

    res_size = resource_size(res);

    /*将寄存器地址映射为虚拟内存*/

    if (!request_mem_region(res->start, res_size, DRIVER_NAME)) {

        ret = -EBUSY;

        goto fail0;

    }

 

    base = ioremap(res->start, res_size);

    if (!base) {

        dev_err(&pdev->dev, "ioremap failed\n");

        ret = -EIO;

        goto fail1;

    }

   

    i2c_imx = kzalloc(sizeof(struct imx_i2c_struct), GFP_KERNEL);

    if (!i2c_imx) {

        dev_err(&pdev->dev, "can‘t allocate interface\n");

        ret = -ENOMEM;

        goto fail2;

    }

 

    /* Setup i2c_imx driver structure */

    strcpy(i2c_imx->adapter.name, pdev->name);

    i2c_imx->adapter.owner      = THIS_MODULE;

    i2c_imx->adapter.algo       = &i2c_imx_algo; /*算法层:这个是跟具体硬件和协议相关的,下面分析*/

    i2c_imx->adapter.dev.parent = &pdev->dev;

    i2c_imx->adapter.nr         = pdev->id;          /*名字*/

    i2c_imx->irq            = irq;

    i2c_imx->base           = base;

    i2c_imx->res            = res;

 

    /* 设置I2C时钟 */

    i2c_imx->clk = clk_get(&pdev->dev, "i2c_clk");

    if (IS_ERR(i2c_imx->clk)) {

        ret = PTR_ERR(i2c_imx->clk);

        dev_err(&pdev->dev, "can‘t get I2C clock\n");

        goto fail3;

    }

 

    /* 设置中断 */

    ret = request_irq(i2c_imx->irq, i2c_imx_isr, 0, pdev->name, i2c_imx);

    if (ret) {

        dev_err(&pdev->dev, "can‘t claim irq %d\n", i2c_imx->irq);

        goto fail4;

    }

 

    /* 等待队列 */

    init_waitqueue_head(&i2c_imx->queue);

 

    /* 设置适配器数据 */

    i2c_set_adapdata(&i2c_imx->adapter, i2c_imx);

 

    /* 设置时钟分频 */

    if (pdata && pdata->bitrate)

        i2c_imx_set_clk(i2c_imx, pdata->bitrate);

    else

        i2c_imx_set_clk(i2c_imx, IMX_I2C_BIT_RATE);

 

    /* 设置I2C的寄存器 */

    writeb(0, i2c_imx->base + IMX_I2C_I2CR);

    writeb(0, i2c_imx->base + IMX_I2C_I2SR);

 

    /* 注册适配器驱动:这个我们后面会分析 */

    ret = i2c_add_numbered_adapter(&i2c_imx->adapter);

    if (ret < 0) {

        dev_err(&pdev->dev, "registration failed\n");

        goto fail5;

    }

 

    /* 将i2c_imx数据放入平台总线中 */

    platform_set_drvdata(pdev, i2c_imx);

 

    dev_dbg(&i2c_imx->adapter.dev, "claimed irq %d\n", i2c_imx->irq);

    dev_dbg(&i2c_imx->adapter.dev, "device resources from 0x%x to 0x%x\n",

        i2c_imx->res->start, i2c_imx->res->end);

    dev_dbg(&i2c_imx->adapter.dev, "allocated %d bytes at 0x%x \n",

        res_size, i2c_imx->res->start);

    dev_dbg(&i2c_imx->adapter.dev, "adapter name: \"%s\"\n",

        i2c_imx->adapter.name);

    dev_dbg(&i2c_imx->adapter.dev, "IMX I2C adapter registered\n");

 

    return 0;   /* Return OK */

 

fail5:

    free_irq(i2c_imx->irq, i2c_imx);

fail4:

    clk_put(i2c_imx->clk);

fail3:

    kfree(i2c_imx);

fail2:

    iounmap(base);

fail1:

    release_mem_region(res->start, resource_size(res));

fail0:

    if (pdata && pdata->exit)

        pdata->exit(&pdev->dev);

    return ret; /* Return error number */

}

 

 

/*这个函数的主要功能是提供该适配器能提供的I2C功能,提供判断*/

static u32 i2c_imx_func(struct i2c_adapter *adapter)

{

    return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;

}

 

static struct i2c_algorithm i2c_imx_algo = {

    .master_xfer    = i2c_imx_xfer,    /*具体的传输的实现*/

    .functionality  = i2c_imx_func  /*传输的方式*/

};

 

static int i2c_imx_xfer(struct i2c_adapter *adapter,

                        struct i2c_msg *msgs, int num)

{

    unsigned int i, temp;

int result;

/*将该适配器上的数据取出来*/

    struct imx_i2c_struct *i2c_imx = i2c_get_adapdata(adapter);

   

    dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__);

 

    /* I2C起始位 */

    result = i2c_imx_start(i2c_imx);

    if (result)

        goto fail0;

 

    /* 对数据进行读写 */

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

        if (i) {

            dev_dbg(&i2c_imx->adapter.dev,

                "<%s> repeated start\n", __func__);

            temp = readb(i2c_imx->base + IMX_I2C_I2CR);

            temp |= I2CR_RSTA;

            writeb(temp, i2c_imx->base + IMX_I2C_I2CR);

            result =  i2c_imx_bus_busy(i2c_imx, 1);

            if (result)

                goto fail0;

        }

        dev_dbg(&i2c_imx->adapter.dev,

            "<%s> transfer message: %d\n", __func__, i);

        /* write/read data */

#ifdef CONFIG_I2C_DEBUG_BUS

        temp = readb(i2c_imx->base + IMX_I2C_I2CR);

        dev_dbg(&i2c_imx->adapter.dev, "<%s> CONTROL: IEN=%d, IIEN=%d, "

            "MSTA=%d, MTX=%d, TXAK=%d, RSTA=%d\n", __func__,

            (temp & I2CR_IEN ? 1 : 0), (temp & I2CR_IIEN ? 1 : 0),

            (temp & I2CR_MSTA ? 1 : 0), (temp & I2CR_MTX ? 1 : 0),

            (temp & I2CR_TXAK ? 1 : 0), (temp & I2CR_RSTA ? 1 : 0));

        temp = readb(i2c_imx->base + IMX_I2C_I2SR);

        dev_dbg(&i2c_imx->adapter.dev,

            "<%s> STATUS: ICF=%d, IAAS=%d, IBB=%d, "

            "IAL=%d, SRW=%d, IIF=%d, RXAK=%d\n", __func__,

            (temp & I2SR_ICF ? 1 : 0), (temp & I2SR_IAAS ? 1 : 0),

            (temp & I2SR_IBB ? 1 : 0), (temp & I2SR_IAL ? 1 : 0),

            (temp & I2SR_SRW ? 1 : 0), (temp & I2SR_IIF ? 1 : 0),

            (temp & I2SR_RXAK ? 1 : 0));

#endif

        if (msgs[i].flags & I2C_M_RD)

            result = i2c_imx_read(i2c_imx, &msgs[i]);

        else

            result = i2c_imx_write(i2c_imx, &msgs[i]);

        if (result)

            goto fail0;

    }

 

fail0:

    /* 停止位 */

    i2c_imx_stop(i2c_imx);

 

    dev_dbg(&i2c_imx->adapter.dev, "<%s> exit with: %s: %d\n", __func__,

        (result < 0) ? "error" : "success msg",

            (result < 0) ? result : num);

    return (result < 0) ? result : num;

}

 

这个函数里面得到所有函数就是最底层真正对硬件的操作了,比如:

static int i2c_imx_start(struct imx_i2c_struct *i2c_imx)

{

    unsigned int temp = 0;

    struct imxi2c_platform_data *pdata;

    int result;

 

    dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__);

 

    /* Currently on Arik/Rigel, the I2C clk is from IPG_PERCLK which is

     * sourced from IPG_CLK. In low bus freq mode, IPG_CLK is at 12MHz

     * and IPG_PERCLK is down to 4MHz.

     * Update I2C divider before set i2c clock.

     */

    pdata = i2c_imx->adapter.dev.parent->platform_data;

    if (pdata && pdata->bitrate)

        i2c_imx_set_clk(i2c_imx, pdata->bitrate);

    else

        i2c_imx_set_clk(i2c_imx, IMX_I2C_BIT_RATE);

 

    clk_enable(i2c_imx->clk);

    writeb(i2c_imx->ifdr, i2c_imx->base + IMX_I2C_IFDR);

    /* Enable I2C controller */

    writeb(0, i2c_imx->base + IMX_I2C_I2SR);

    writeb(I2CR_IEN, i2c_imx->base + IMX_I2C_I2CR);

 

    /* Wait controller to be stable */

    udelay(50);

 

    /* Start I2C transaction */

    temp = readb(i2c_imx->base + IMX_I2C_I2CR);

    temp |= I2CR_MSTA;

    writeb(temp, i2c_imx->base + IMX_I2C_I2CR);

    result = i2c_imx_bus_busy(i2c_imx, 1);

    if (result)

        return result;

    i2c_imx->stopped = 0;

 

    temp |= I2CR_IIEN | I2CR_MTX | I2CR_TXAK;

    writeb(temp, i2c_imx->base + IMX_I2C_I2CR);

    return result;

}

 

在i2c_add_numbered_adapter中会对最后的适配器驱动进行注册,在注册之前还会对I2C设备驱动进行注册。

int i2c_add_numbered_adapter(struct i2c_adapter *adap)

{

    int id;

    int status;

 

    if (adap->nr & ~MAX_ID_MASK)

        return -EINVAL;

 

retry:

    if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0)

        return -ENOMEM;

 

    mutex_lock(&core_lock);

    /* "above" here means "above or equal to", sigh;

     * we need the "equal to" result to force the result

     */

    status = idr_get_new_above(&i2c_adapter_idr, adap, adap->nr, &id);

    if (status == 0 && id != adap->nr) {

        status = -EBUSY;

        idr_remove(&i2c_adapter_idr, id);

    }

    mutex_unlock(&core_lock);

    if (status == -EAGAIN)

        goto retry;

 

if (status == 0)

    /*在这里真正注册I2C适配器*/

        status = i2c_register_adapter(adap);

    return status;

}

 

 

static int i2c_register_adapter(struct i2c_adapter *adap)

{

    int res = 0;

 

    /* Can‘t register until after driver model init */

    if (unlikely(WARN_ON(!i2c_bus_type.p))) {

        res = -EAGAIN;

        goto out_list;

    }

 

    /* Sanity checks */

    if (unlikely(adap->name[0] == ‘\0‘)) {

        pr_err("i2c-core: Attempt to register an adapter with "

               "no name!\n");

        return -EINVAL;

    }

    if (unlikely(!adap->algo)) {

        pr_err("i2c-core: Attempt to register adapter ‘%s‘ with "

               "no algo!\n", adap->name);

        return -EINVAL;

    }

 

    rt_mutex_init(&adap->bus_lock);

    mutex_init(&adap->userspace_clients_lock);

    INIT_LIST_HEAD(&adap->userspace_clients);

 

    /* Set default timeout to 1 second if not already set */

    if (adap->timeout == 0)

        adap->timeout = HZ;

 

    dev_set_name(&adap->dev, "i2c-%d", adap->nr);

    adap->dev.bus = &i2c_bus_type;

    adap->dev.type = &i2c_adapter_type;

    res = device_register(&adap->dev);    /*注册*/

    if (res)

        goto out_list;

 

    dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name);

 

#ifdef CONFIG_I2C_COMPAT

    res = class_compat_create_link(i2c_adapter_compat_class, &adap->dev,

                       adap->dev.parent);

    if (res)

        dev_warn(&adap->dev,

             "Failed to create compatibility class link\n");

#endif

 

    /* 在这里对已经添加好的I2C设备进行注册 */

    if (adap->nr < __i2c_first_dynamic_bus_num)

        i2c_scan_static_board_info(adap);

 

    /* Notify drivers */

    mutex_lock(&core_lock);

    bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter);

    mutex_unlock(&core_lock);

 

    return 0;

 

out_list:

    mutex_lock(&core_lock);

    idr_remove(&i2c_adapter_idr, adap->nr);

    mutex_unlock(&core_lock);

    return res;

}

 

 

static void i2c_scan_static_board_info(struct i2c_adapter *adapter)

{

    struct i2c_devinfo  *devinfo;

 

    down_read(&__i2c_board_lock);

    list_for_each_entry(devinfo, &__i2c_board_list, list) {

        if (devinfo->busnum == adapter->nr

                && !i2c_new_device(adapter,

                        &devinfo->board_info))

            dev_err(&adapter->dev,

                "Can‘t create device at 0x%02x\n",

                devinfo->board_info.addr);

    }

    up_read(&__i2c_board_lock);

}

 

3.  I2C从设备的添加

int __init

i2c_register_board_info(int busnum,

    struct i2c_board_info const *info, unsigned len)

{

    int status;

   

    down_write(&__i2c_board_lock);

 

    /* */

    if (busnum >= __i2c_first_dynamic_bus_num)

        __i2c_first_dynamic_bus_num = busnum + 1;

 

    for (status = 0; len; len--, info++) {

        struct i2c_devinfo  *devinfo;

 

        devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL);

        if (!devinfo) {

            pr_debug("i2c-core: can‘t register boardinfo!\n");

            status = -ENOMEM;

            break;

        }

       /*放在哪个适配器上*/

        devinfo->busnum = busnum;

        devinfo->board_info = *info;

        /*将所有的从设备都挂在一个链表中*/

        list_add_tail(&devinfo->list, &__i2c_board_list);

    }

    up_write(&__i2c_board_lock);

 

    return status;

}

 

4.  AT24对于I2C的读写

只研究I2C,所以只是看下I2C在AT24C02中大概是如何使用,其他代码都省略

static struct i2c_driver at24_driver = {

    .driver = {

        .name = "at24",

        .owner = THIS_MODULE,

    },

    .probe = at24_probe,

    .remove = __devexit_p(at24_remove),

    .id_table = at24_ids,     /*这是一个包含所有支持名字的列表,只要注册的设备里有其中一个名字匹配就匹配成功*/

};

 

static int __init at24_init(void)

{

    。。。省略。。。

    /*注册从设备驱动*/

    return i2c_add_driver(&at24_driver);

}

module_init(at24_init);

 

static inline int i2c_add_driver(struct i2c_driver *driver)

{

    return i2c_register_driver(THIS_MODULE, driver);

}

从设备的注册函数如下:

int i2c_register_driver(struct module *owner, struct i2c_driver *driver)

{

    int res;

 

    /* Can‘t register until after driver model init */

    if (unlikely(WARN_ON(!i2c_bus_type.p)))

        return -EAGAIN;

 

    /* add the driver to the list of i2c drivers in the driver core */

    driver->driver.owner = owner;

    driver->driver.bus = &i2c_bus_type;

 

    /*注册设备*/

    res = driver_register(&driver->driver);

    if (res)

        return res;

 

    /* Drivers should switch to dev_pm_ops instead. */

    if (driver->suspend)

        pr_warn("i2c-core: driver [%s] using legacy suspend method\n",

            driver->driver.name);

    if (driver->resume)

        pr_warn("i2c-core: driver [%s] using legacy resume method\n",

            driver->driver.name);

 

    pr_debug("i2c-core: driver [%s] registered\n", driver->driver.name);

 

    INIT_LIST_HEAD(&driver->clients);

    /* Walk the adapters that are already present */

    i2c_for_each_dev(driver, __process_new_driver);

 

    return 0;

}

 

static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)

{

    if (client->dev.platform_data) {

        chip = *(struct at24_platform_data *)client->dev.platform_data;

    } else {

        if (!id->driver_data) {

            err = -ENODEV;

            goto err_out;

        }

     

    /* Use I2C operations unless we‘re stuck with SMBus extensions. */

    if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {

        if (chip.flags & AT24_FLAG_ADDR16) {

            err = -EPFNOSUPPORT;

            goto err_out;

        }

       /*查看支持的模式*/

        if (i2c_check_functionality(client->adapter,

                I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {

            use_smbus = I2C_SMBUS_I2C_BLOCK_DATA;

        } else if (i2c_check_functionality(client->adapter,

                I2C_FUNC_SMBUS_READ_WORD_DATA)) {

            use_smbus = I2C_SMBUS_WORD_DATA;

        } else if (i2c_check_functionality(client->adapter,

                I2C_FUNC_SMBUS_READ_BYTE_DATA)) {

            use_smbus = I2C_SMBUS_BYTE_DATA;

        } else {

            err = -EPFNOSUPPORT;

            goto err_out;

        }

    }

 

    if (chip.flags & AT24_FLAG_TAKE8ADDR)

        num_addresses = 8;

    else

        num_addresses = DIV_ROUND_UP(chip.byte_len,

            (chip.flags & AT24_FLAG_ADDR16) ? 65536 : 256);

 

    at24 = kzalloc(sizeof(struct at24_data) +

        num_addresses * sizeof(struct i2c_client *), GFP_KERNEL);

    if (!at24) {

        err = -ENOMEM;

        goto err_out;

    } mutex_init(&at24->lock);

    at24->use_smbus = use_smbus;

    at24->chip = chip;

    at24->num_addresses = num_addresses;

 

    /*

     * Export the EEPROM bytes through sysfs, since that‘s convenient.

     * By default, only root should see the data (maybe passwords etc)

     */

    sysfs_bin_attr_init(&at24->bin);

    at24->bin.attr.name = "eeprom";

    at24->bin.attr.mode = chip.flags & AT24_FLAG_IRUGO ? S_IRUGO : S_IRUSR;

    at24->bin.read = at24_bin_read;

    at24->bin.size = chip.byte_len;

 

    at24->macc.read = at24_macc_read;

 

    writable = !(chip.flags & AT24_FLAG_READONLY);

    if (writable) {

        if (!use_smbus || i2c_check_functionality(client->adapter,

                I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)) {

 

            unsigned write_max = chip.page_size;

 

            at24->macc.write = at24_macc_write;

 

            at24->bin.write = at24_bin_write;

            at24->bin.attr.mode |= S_IWUSR;

 

            if (write_max > io_limit)

                write_max = io_limit;

            if (use_smbus && write_max > I2C_SMBUS_BLOCK_MAX)

                write_max = I2C_SMBUS_BLOCK_MAX;

            at24->write_max = write_max;

 

            /* buffer (data + address at the beginning) */

            at24->writebuf = kmalloc(write_max + 2, GFP_KERNEL);

            if (!at24->writebuf) {

                err = -ENOMEM;

                goto err_struct;

            }

        } else {

            dev_warn(&client->dev,

                "cannot write due to controller restrictions.");

        }

    }

 

}

 

 

 

static ssize_t at24_bin_read(struct file *filp, struct kobject *kobj,

        struct bin_attribute *attr,

        char *buf, loff_t off, size_t count)

{

    struct at24_data *at24;

 

    at24 = dev_get_drvdata(container_of(kobj, struct device, kobj));

    return at24_read(at24, buf, off, count);

}

然后一直往下追,在某部分功能会看到

static ssize_t at24_eeprom_read(struct at24_data *at24, char *buf,

        unsigned offset, size_t count)

{ 

      status = i2c_transfer(client->adapter, msg, 2);

}

 

drivers.c/i2c/i2c-core.c

 

int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)

{

    unsigned long orig_jiffies;

    int ret, try;

 

    if (adap->algo->master_xfer) {

        if (in_atomic() || irqs_disabled()) {

            ret = i2c_trylock_adapter(adap);

            if (!ret)

                /* I2C activity is ongoing. */

                return -EAGAIN;

        } else {

            i2c_lock_adapter(adap);

        }

 

        /* Retry automatically on arbitration loss */

        orig_jiffies = jiffies;

        for (ret = 0, try = 0; try <= adap->retries; try++) {

            ret = adap->algo->master_xfer(adap, msgs, num);

            if (ret != -EAGAIN)

                break;

            if (time_after(jiffies, orig_jiffies + adap->timeout))

                break;

        }

        i2c_unlock_adapter(adap);

 

        return ret;

    } else {

        dev_dbg(&adap->dev, "I2C level transfers not supported\n");

        return -EOPNOTSUPP;

    }

}

 

在这里,我们看到了adap->algo->master_xfer,也就是底层最终的传输函数。

以上是关于Kernel的IIC驱动分析的主要内容,如果未能解决你的问题,请参考以下文章

九IIC驱动原理分析

驱动程序实例:mpu6050(IIC + cdev)

28.Linux-IIC驱动(详解)

2019 5 19 IIC驱动程序

最全的IIC介绍及其设备驱动编写

四针OLED驱动代码——IIC驱动