Linux下DM644x设备驱动I2C之总线驱动(一)详解
2012-07-04 22:04
423 查看
本文均属自己阅读源码的点滴总结,转账请注明出处谢谢。
欢迎和大家交流。qq:1037701636 email:200803090209@zjut.com,gzzaigcn2012@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如下:
操作主要围绕以下4个结构体展开:
结合以上两段代码,可以知道以下内容
1 .在模块初始化函数中完成适配器与设备。的连接了设备驱动davinci_i2c_driver和平台设备davinci_i2c_device的注册
2.获取I2C相关端口的申请,I2C相应寄存器的物理首地址0x01C21000,完成了I2C模块初始化操作以及IRQ_I2C=39的中断申请。
在这里对I2C模块的初始化进行解析,内容如下:
主要完成Dm6441上I2C的总线时钟的配置。以及做一些中断使能的初始化。在这里都是对dev->regs这个结构体指针的内容
该结构体的实际内容如下:
涉及的内容是DM6441 I2C模块的相关寄存器。在获取寄存器的物理首地址之后,就可以直接对这个结构体实例的内容写数值,该结构体被包含在以下结构体中
这里的另一块内容是添加适配器到I2C驱动中使用i2c_add_adapter函数完成i2c_davinci_adap(该结构体实例内容见上述代码)
在该函数内主要完成适配器结构体中成员变量设备dev的初始化,紧接着完成适配器这个抽象设备的注册,再完成适配器设备类的注册以及相关设备节点的生成。
欢迎和大家交流。qq:1037701636 email:200803090209@zjut.com,gzzaigcn2012@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的初始化,紧接着完成适配器这个抽象设备的注册,再完成适配器设备类的注册以及相关设备节点的生成。
相关文章推荐
- Linux I2C核心、总线与设备驱动
- Linux SPI总线设备驱动模型详解
- 迅为4412开发板Linux驱动教程——总线_设备_驱动注册流程详解
- Linux I2C核心、总线与设备驱动
- 迅为Linux驱动教程——总线_设备_驱动注册流程详解
- 迅为Linux驱动教程——总线_设备_驱动注册流程详解
- 老查的ARM学习笔记:chapter-2(linux总线设备驱动详解)
- Linux I2C子系统分析-I2C总线驱动&&Linux I2C子系统分析-I2C设备驱动
- Linux I2C核心、总线与设备驱动
- Linux I2C 核心、总线、与设备驱动
- 迅为4412开发板Linux驱动教程——总线_设备_驱动注册流程详解
- Linux I2C核心、总线与设备驱动(一)
- Linux设备驱动之——I2C总线
- Linux SPI总线设备驱动模型详解
- Linux设备驱动之——I2C总线
- 第十五章 Linux I2C核心、总线与设备驱动
- 第15章 Linux的I2C核心、总线与设备驱动
- Linux I2C核心、总线与设备驱动I
- Linux&nbsp;I2C核心、总线与设备驱动
- Linux设备驱动之——I2C总线