金牌会员
- 积分
- 1255
- 金钱
- 1255
- 注册时间
- 2017-11-18
- 在线时间
- 296 小时
|
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 */
}
};
|
|