您的位置:首页 > 运维架构 > Linux

Linux下DM644x设备驱动I2C之总线驱动(一)详解

2012-07-04 22:04 423 查看
本文均属自己阅读源码的点滴总结,转账请注明出处谢谢。

欢迎和大家交流。qq:1037701636 email:200803090209@zjut.comgzzaigcn2012@gmail.com

 
linux DM6441下I2C设备驱动的开发
前言:
和常用的Linux I2C的驱动开发一样,主要包括总线Bus驱动,以及相应的设备驱动开发。
总线驱动和CPU的关系较为密切,涉及到相关硬件的操作。
本文以Linux2.6.1和Dm6441简单解析一下整个驱动的编写架构。
 
1.   总线驱动
涉及到总线驱动的相关代码在drivers/i2c/bussses下面,该目录下是各CPU的I2C模块涉及到的内容,这里我们选择i2c-davinci.c文件
总线驱动中Init函数i2c_davinci_init如下:
static int __init i2c_davinci_init(void)
{
int status;
struct device 	*dev = NULL;

DEB0("%s %s", __TIME__, __DATE__);

DEB1("i2c_davinci_init()");

#if 0
if (i2c_davinci_busFreq > 200)
i2c_davinci_busFreq = 400;	/*Fast mode */
else
i2c_davinci_busFreq = 100;	/*Standard mode */
#endif

i2c_clock = clk_get (dev, "I2CCLK"); //获取I2C输入时钟批频率27MHz

if (IS_ERR(i2c_clock))
return -1;

clk_use (i2c_clock);
clk_enable (i2c_clock);
i2c_davinci_inputClock = clk_get_rate (i2c_clock);    //获取I2C输入时钟批频率27MHz

DEB1 ("IP CLOCK = %ld", i2c_davinci_inputClock);

memset(&i2c_davinci_dev, 0, sizeof(i2c_davinci_dev));
init_waitqueue_head(&i2c_davinci_dev.cmd_wait);

i2c_davinci_dev.regs = (davinci_i2cregsovly)I2C_BASE;      //I2C控制器相关寄存器所在地址

status = (int)request_region(I2C_BASE, I2C_IOSIZE, MODULE_NAME); //申请一块IO输入输出区域#define MODULE_NAME "DaVinci I2C"
if (!status) {
i2c_err("I2C is already in use\n");
return -ENODEV;
}

status = request_irq(IRQ_I2C, i2c_davinci_isr, 0, "i2c",
&i2c_davinci_dev);  //申请I2C中断号IRQ_I2C=39
if (status) {
i2c_err("failed to request I2C IRQ");
goto do_release_region;
}

i2c_set_adapdata(&i2c_davinci_adap, &i2c_davinci_dev);  //adap设备的驱动程序私有数据i2c_davinci_adap->dev->driver_data =i2c_davinci_dev
status = i2c_add_adapter(&i2c_davinci_adap);    //I2C上添加adapter
if (status) {
i2c_err("failed to add adapter");
goto do_free_irq;
}

i2c_davinci_reset(&i2c_davinci_dev);   //完成I2C相关寄存器的初始化

if (driver_register(&davinci_i2c_driver) != 0)            // I2C驱动注册
printk(KERN_ERR "Driver register failed for davinci_i2c\n");
if (platform_device_register(&davinci_i2c_device) != 0) {
printk(KERN_ERR "Device register failed for i2c\n");
driver_unregister(&davinci_i2c_driver);
}

return 0;

do_free_irq:
free_irq(IRQ_I2C, &i2c_davinci_dev);
do_release_region:
release_region(I2C_BASE, I2C_IOSIZE);

return status;
}

操作主要围绕以下4个结构体展开:

static struct i2c_algorithm i2c_davinci_algo = {            //主要涉及相关的I2C通信协议以及数据的传输和接受
.name = "DAVINCI I2C algorithm",
.id = I2C_ALGO_EXP,
.master_xfer = i2c_davinci_xfer,
.smbus_xfer = NULL,
.slave_send = NULL,
.slave_recv = NULL,
.algo_control = NULL,
.functionality = i2c_davinci_func,
};

static struct i2c_adapter i2c_davinci_adap = {       //对I2C通信协议的封装,定义的适配器实例
.owner = THIS_MODULE,
.name = "DAVINCI I2C adapter",
.id = I2C_ALGO_EXP,
.algo = &i2c_davinci_algo,
.algo_data = NULL,
.client_register = NULL,
.client_unregister = NULL,
};

static struct device_driver davinci_i2c_driver = {            //达芬奇的设备驱动
.name = "davinci_i2c",
.bus = &platform_bus_type,
.remove = davinci_i2c_remove,
};

static struct platform_device davinci_i2c_device = {         //定义的达芬奇平台设备
.name = "i2c",
.id = -1,
.dev = {
.driver = &davinci_i2c_driver,
.release = davinci_i2c_device_release,
},
};

结合以上两段代码,可以知道以下内容

1 .在模块初始化函数中完成适配器与设备。的连接了设备驱动davinci_i2c_driver和平台设备davinci_i2c_device的注册

2.获取I2C相关端口的申请,I2C相应寄存器的物理首地址0x01C21000,完成了I2C模块初始化操作以及IRQ_I2C=39的中断申请。

在这里对I2C模块的初始化进行解析,内容如下:

static int i2c_davinci_reset(struct i2c_davinci_device *dev)
{
u16 psc;
u32 clk;

DEB1("i2c: reset called");

/* put I2C into reset */
dev->regs->icmdr &= ~DAVINCI_I2C_ICMDR_IRS_MASK; //首先需复位I2C模块

/* NOTE: I2C Clock divider programming info
* As per I2C specs the following formulas provide prescalar
* and low/high divider values
*
* input clk --> PSC Div -----------> ICCL/H Div --> output clock
*                       module clk
*
* output clk = module clk / (PSC + 1) [ (ICCL + d) + (ICCH + d) ]
*
* Thus,
* (ICCL + ICCH) = clk = (input clk / ((psc +1) * output clk)) - 2d;
*
* where if PSC == 0, d = 7,
*       if PSC == 1, d = 6
*       if PSC > 1 , d = 5
*/

psc = 26; /* To get 1MHz clock */

clk = ((i2c_davinci_inputClock/(psc + 1)) / (i2c_davinci_busFreq * 1000)) - 10; //i2c_davinci_busFreq=20

dev->regs->icpsc = psc;  //PSC寄存器写26,即预分频数27
dev->regs->icclkh = (50 * clk) / 100; /* duty cycle should be 50% */
dev->regs->icclkl = (clk - dev->regs->icclkh); //高低电平配置内容

DEB1("CLK  = %d", clk);
DEB1("PSC  = %d", dev->regs->icpsc);
DEB1("CLKL = %d", dev->regs->icclkl);
DEB1("CLKH = %d", dev->regs->icclkh);

/* Set Own Address: */
dev->regs->icoar = i2c_davinci_own_addr; //oxa

/* Enable interrupts */
dev->regs->icimr = I2C_DAVINCI_INTR_ALL;   //所有中断都使能

/* Take the I2C module out of reset: */
dev->regs->icmdr |= DAVINCI_I2C_ICMDR_IRS_MASK;    //使能I2C模块

return 0;
}

主要完成Dm6441上I2C的总线时钟的配置。以及做一些中断使能的初始化。在这里都是对dev->regs这个结构体指针的内容

该结构体的实际内容如下:

typedef struct {
u16 icoar;
u8 rsvd0[2];
u16 icimr;
u8 rsvd1[2];
u16 icstr;
u8 rsvd2[2];
u16 icclkl;
u8 rsvd3[2];
u16 icclkh;
u8 rsvd4[2];
u16 iccnt;
u8 rsvd5[2];
u16 icdrr;
u8 rsvd6[2];
u16 icsar;
u8 rsvd7[2];
u16 icdxr;
u8 rsvd8[2];
u16 icmdr;
u8 rsvd9[2];
u16 icivr;
u8 rsvd10[2];
u16 icemdr;
u8 rsvd11[2];
u16 icpsc;
u8 rsvd12[2];
u16 icpid1;
u8 rsvd13[2];
u16 icpid2;
u8 rsvd14[14];
u32 ipcfunc;
u32 icpdir;
u32 icpdin;
u32 icpdout;
u32 icpdset;
u32 icpdclr;
} davinci_i2cregs;

/**************************************************************************\
* Overlay structure typedef definition
\**************************************************************************/
typedef volatile davinci_i2cregs *davinci_i2cregsovly;

涉及的内容是DM6441 I2C模块的相关寄存器。在获取寄存器的物理首地址之后,就可以直接对这个结构体实例的内容写数值,该结构体被包含在以下结构体中

struct i2c_davinci_device {
int cmd_complete, cmd_err;
wait_queue_head_t cmd_wait;
u8 *buf;
size_t buf_len;
davinci_i2cregsovly regs;
};

 

这里的另一块内容是添加适配器到I2C驱动中使用i2c_add_adapter函数完成i2c_davinci_adap(该结构体实例内容见上述代码)

int i2c_add_adapter(struct i2c_adapter *adap)
{
int id, res = 0;
struct list_head   *item;
struct i2c_driver  *driver;

down(&core_lists);

if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0) {
res = -ENOMEM;
goto out_unlock;
}

res = idr_get_new(&i2c_adapter_idr, NULL, &id);
if (res < 0) {
if (res == -EAGAIN)
res = -ENOMEM;
goto out_unlock;
}

adap->nr =  id & MAX_ID_MASK;
init_MUTEX(&adap->bus_lock);
init_MUTEX(&adap->clist_lock);
list_add_tail(&adap->list,&adapters);
INIT_LIST_HEAD(&adap->clients);

/* Add the adapter to the driver core.
* If the parent pointer is not set up,
* we add this adapter to the host bus.
*/
if (adap->dev.parent == NULL)
adap->dev.parent = &platform_bus;
sprintf(adap->dev.bus_id, "i2c-%d", adap->nr);
adap->dev.driver = &i2c_adapter_driver;   //适配器的设备驱动指向i2c_adapter_driver
adap->dev.release = &i2c_adapter_dev_release;
device_register(&adap->dev);                       //适配器设备注册
device_create_file(&adap->dev, &dev_attr_name);

/* Add this adapter to the i2c_adapter class */
memset(&adap->class_dev, 0x00, sizeof(struct class_device));
adap->class_dev.dev = &adap->dev;
adap->class_dev.class = &i2c_adapter_class;
strlcpy(adap->class_dev.class_id, adap->dev.bus_id, BUS_ID_SIZE);
class_device_register(&adap->class_dev);

/* inform drivers of new adapters */
list_for_each(item,&drivers) {
driver = list_entry(item, struct i2c_driver, list);
if (driver->flags & I2C_DF_NOTIFY)
/* We ignore the return code; if it fails, too bad */
driver->attach_adapter(adap);
}

dev_dbg(&adap->dev, "registered as adapter #%d\n", adap->nr);

out_unlock:
up(&core_lists);
return res;
}

在该函数内主要完成适配器结构体中成员变量设备dev的初始化,紧接着完成适配器这个抽象设备的注册,再完成适配器设备类的注册以及相关设备节点的生成。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息