OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 3670|回复: 0

I.MX6Q I2C适配器驱动分析

[复制链接]

82

主题

589

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1255
金钱
1255
注册时间
2017-11-18
在线时间
296 小时
发表于 2020-5-3 21:36:47 | 显示全部楼层 |阅读模式
I.X6Q的I2C适配器驱动(3.0.35版本内核)
入口函数(可以预知I2C总线内部也是platform总线)
static int __init i2c_adap_imx_init(void)
{
    return platform_driver_probe(&i2c_imx_driver, i2c_imx_probe);
}

static struct platform_driver i2c_imx_driver = {
    .remove        = __exit_p(i2c_imx_remove),
    .driver    = {
        .name    = DRIVER_NAME,
        .owner    = THIS_MODULE,
    }
    //这个驱动没有使用id匹配表吗??为什么?
};

跳转分析i2c_imx_probe(当设备和驱动得到匹配的时候,这个函数得到调用)
static int __init i2c_imx_probe(struct platform_device *pdev)
{
    ...
    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);    //获取资源
    ...
    irq = platform_get_irq(pdev, 0);                        //获取中断号
    ...
    //得到I2C适配器地址并映射成虚拟地址
    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);
    ...
    //NXP使用imx_i2c_struct结构体来表示I.MX系列SOC的I2C控制器
    //设置adapter(控制器)
    strcpy(i2c_imx->adapter.name, pdev->name);
    i2c_imx->adapter.owner        = THIS_MODULE;
    i2c_imx->adapter.algo        = &i2c_imx_algo;//设置i2c通信算法函数
    i2c_imx->adapter.dev.parent    = &pdev->dev;
    i2c_imx->adapter.nr         = pdev->id;
    i2c_imx->irq                = irq;
    i2c_imx->base                = base;            //前面得到地址映射了(赋值I2C控制器的虚拟地址)
    i2c_imx->res                = res;
    ...
    /* Request IRQ *///注册中断,i2c_imx_isr是中断处理函数
    ret = request_irq(i2c_imx->irq, i2c_imx_isr, 0, pdev->name, i2c_imx);
    ...
    init_waitqueue_head(&i2c_imx->queue);        //初始化队列头

    /* Set up adapter data */
    i2c_set_adapdata(&i2c_imx->adapter, i2c_imx);
    ...
    /* Set up chip registers to defaults */
    writeb(0, i2c_imx->base + IMX_I2C_I2CR);
    writeb(0, i2c_imx->base + IMX_I2C_I2SR);

    /* Add I2C adapter */
    ret = i2c_add_numbered_adapter(&i2c_imx->adapter);//向内核注册初始化好的I2C适配器,这个函数在i2c-core.c文件里面定义(属于Linux平台的函数,跟具体的芯片无关)
    ...
    /* Set up platform driver data */
    platform_set_drvdata(pdev, i2c_imx);
    //4.0.15版本的内核,NXP是支持I2C使用DMA的,但是3.0.35好像不支持
    return 0;   /* Return OK */
}
整体上看,i2c_imx_probe函数就就只做了一件事情,就是获取I2C资源信息然后初始化I2C适配器,向内核注册初始化好的I2C适配器。
前面i2c_imx->adapter.algo        = &i2c_imx_algo;//设置i2c通信算法函数
我们转去看一下这个函数
static struct i2c_algorithm i2c_imx_algo = {
    .master_xfer    = i2c_imx_xfer,//通过这个函数来完成和I2C器件的通信
    .functionality    = i2c_imx_func,//这个函数用于返回i2c支持什么通信协议
};

//看一下这个传输函数i2c_imx_xfer
static int i2c_imx_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num)
{
    ...
    //开启i2c通信
    result = i2c_imx_start(i2c_imx);
    ...
    /* read/write data *///没有看明白这个操作的含义
    for (i = 0; i < num; i++) {
        if (i) {
            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;
        }
        ...
    }

    //NXP在4.0.15版本的内核还支持i2使用dma来写的方式
        if (msgs[i].flags & I2C_M_RD)                 //如果是读数据
            result = i2c_imx_read(i2c_imx, &msgs[i]);//读数据,数据类型是struct i2c_msg *msgs
        else
            result = i2c_imx_write(i2c_imx, &msgs[i]);//写数据,数据类型是struct i2c_msg *msgs
        if (result)
            goto fail0;

        /* Stop I2C transfer */
    i2c_imx_stop(i2c_imx);//停止i2c通信
}
//分析I2C读写函数
static int i2c_imx_read(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs)
{
    ...
    writeb((msgs->addr << 1) | 0x01, i2c_imx->base + IMX_I2C_I2DR);    //发送i2c器件地址,同时发送0x01,表示读数据
    result = i2c_imx_trx_complete(i2c_imx);                            //判断发送是否完成
    if (result)
        return result;
    result = i2c_imx_acked(i2c_imx);                                //等待应答
    if (result)
        return result;
    /* setup bus to read data */
    temp = readb(i2c_imx->base + IMX_I2C_I2CR);
    temp &= ~I2CR_MTX;
    if (msgs->len - 1)
        temp &= ~I2CR_TXAK;
    writeb(temp, i2c_imx->base + IMX_I2C_I2CR);
    readb(i2c_imx->base + IMX_I2C_I2DR); /* dummy read */

    //使用循环来读取数据
    for (i = 0; i < msgs->len; i++) {
        result = i2c_imx_trx_complete(i2c_imx);
        if (result)
            return result;
        //如果读到最后一个数据,则先i2c_imx->stopped = 1;
        if (i == (msgs->len - 1)) {
            /* It must generate STOP before read I2DR to prevent
               controller from generating another clock cycle */
            temp = readb(i2c_imx->base + IMX_I2C_I2CR);
            temp &= ~(I2CR_MSTA | I2CR_MTX);
            writeb(temp, i2c_imx->base + IMX_I2C_I2CR);
            i2c_imx_bus_busy(i2c_imx, 0);
            i2c_imx->stopped = 1;
        } else if (i == (msgs->len - 2)) {
            temp = readb(i2c_imx->base + IMX_I2C_I2CR);
            temp |= I2CR_TXAK;
            writeb(temp, i2c_imx->base + IMX_I2C_I2CR);
        }
        //把数据读取到msgs->buf中
        msgs->buf[i] = readb(i2c_imx->base + IMX_I2C_I2DR);
    }
    return 0;
}


static int i2c_imx_write(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs)
{
    ...
    writeb(msgs->addr << 1, i2c_imx->base + IMX_I2C_I2DR);    //发送要写入数据的寄存器地址
    result = i2c_imx_trx_complete(i2c_imx);                    //判断传输是否完成
    if (result)
        return result;
    result = i2c_imx_acked(i2c_imx);                        //等待应答
    if (result)
        return result;

    //使用循环来向数据寄存器写数据
    for (i = 0; i < msgs->len; i++) {
        writeb(msgs->buf[i], i2c_imx->base + IMX_I2C_I2DR);    //循环将数据写到数据I2C的数据寄存器当中
        result = i2c_imx_trx_complete(i2c_imx);                //判断传输是否完成
        if (result)
            return result;
        result = i2c_imx_acked(i2c_imx);                    //等待应答
        if (result)
            return result;
    }
    return 0;
}
//i2c传输的信息结构
struct i2c_msg {
    __u16 addr;    //地址
    __u16 flags;//读写标志
    ...
    __u16 len;    //读写的长度
    __u8 *buf;    //指向要发送的数据地址
};

//下面这段代码是在4.0.15版本的内核出现的,3.0.35版本的内核并没有,请问怎么进行设备和驱动的匹配呢?
static struct platform_device_id imx_i2c_devtype[] = {
    {
        .name = "imx1-i2c",
        .driver_data = (kernel_ulong_t)&imx1_i2c_hwdata,
    },
    {
        .name = "imx21-i2c",
        .driver_data = (kernel_ulong_t)&imx21_i2c_hwdata,
    },
    {
        /* sentinel */
    }
};

没有脑袋
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2024-11-29 04:23

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表