zoukankan      html  css  js  c++  java
  • 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>
    ", __func__);
    
        /*获取来自适配器设备上的内存资源:I2C寄存器地址*/
    
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    
        if (!res) {
    
            dev_err(&pdev->dev, "can't get device resources
    ");
    
            return -ENOENT;
    
    }
    
    /*获取来自适配器设备上的中断号*/
    
        irq = platform_get_irq(pdev, 0);
    
        if (irq < 0) {
    
            dev_err(&pdev->dev, "can't get irq number
    ");
    
            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
    ");
    
            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
    ");
    
            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
    ");
    
            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
    ", 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
    ");
    
            goto fail5;
    
        }
    
     
    
        /* 将i2c_imx数据放入平台总线中 */
    
        platform_set_drvdata(pdev, i2c_imx);
    
     
    
        dev_dbg(&i2c_imx->adapter.dev, "claimed irq %d
    ", i2c_imx->irq);
    
        dev_dbg(&i2c_imx->adapter.dev, "device resources from 0x%x to 0x%x
    ",
    
            i2c_imx->res->start, i2c_imx->res->end);
    
        dev_dbg(&i2c_imx->adapter.dev, "allocated %d bytes at 0x%x 
    ",
    
            res_size, i2c_imx->res->start);
    
        dev_dbg(&i2c_imx->adapter.dev, "adapter name: "%s"
    ",
    
            i2c_imx->adapter.name);
    
        dev_dbg(&i2c_imx->adapter.dev, "IMX I2C adapter registered
    ");
    
     
    
        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>
    ", __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
    ", __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
    ", __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
    ", __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
    ", __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
    ", __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>
    ", __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] == '')) {
    
            pr_err("i2c-core: Attempt to register an adapter with "
    
                   "no name!
    ");
    
            return -EINVAL;
    
        }
    
        if (unlikely(!adap->algo)) {
    
            pr_err("i2c-core: Attempt to register adapter '%s' with "
    
                   "no algo!
    ", 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
    ", 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
    ");
    
    #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
    ",
    
                    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!
    ");
    
                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
    ",
    
                driver->driver.name);
    
        if (driver->resume)
    
            pr_warn("i2c-core: driver [%s] using legacy resume method
    ",
    
                driver->driver.name);
    
     
    
        pr_debug("i2c-core: driver [%s] registered
    ", 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
    ");
    
            return -EOPNOTSUPP;
    
        }
    
    }

     

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

  • 相关阅读:
    多线程:多线程设计模式(一):总体介绍
    javascript:12种JavaScript MVC框架之比较
    mysql 查询死锁语句
    charles 抓包工具破解方法
    java 自定义log类
    git统计日期之间的代码改动行数
    mac/linux自带定时任务执行crontab的使用
    python MD5步骤
    python 操作excel读写
    python logger日志工具类
  • 原文地址:https://www.cnblogs.com/maogefff/p/7639577.html
Copyright © 2011-2022 走看看