歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

Linux下DM644x設備驅動I2C之總線驅動

Linux DM6441下I2C設備驅動的開發 和常用的Linux I2C的驅動開發一樣,主要包括總線Bus驅動,以及相應的設備驅動開發。 總線驅動和CPU的關系較為密切,涉及到相關硬件的操作。 本文以Linux2.6.1和Dm6441簡單解析一下整個驅動的編寫架構。   1.   總線驅動 涉及到總線驅動的相關代碼在drivers/i2c/bussses下面,該目錄下是各CPU的I2C模塊涉及到的內容,這裡我們選擇i2c-davinci.c文件 總線驅動中Init函數i2c_davinci_init如下:
  1. static int __init i2c_davinci_init(void)  
  2. {  
  3.     int status;  
  4.     struct device   *dev = NULL;  
  5.   
  6.     DEB0("%s %s", __TIME__, __DATE__);  
  7.   
  8.     DEB1("i2c_davinci_init()");  
  9.   
  10. #if 0  
  11.     if (i2c_davinci_busFreq > 200)  
  12.         i2c_davinci_busFreq = 400;  /*Fast mode */  
  13.     else  
  14.         i2c_davinci_busFreq = 100;  /*Standard mode */  
  15. #endif  
  16.   
  17.     i2c_clock = clk_get (dev, "I2CCLK"); //獲取I2C輸入時鐘批頻率27MHz  
  18.   
  19.     if (IS_ERR(i2c_clock))  
  20.             return -1;  
  21.   
  22.     clk_use (i2c_clock);  
  23.     clk_enable (i2c_clock);  
  24.     i2c_davinci_inputClock = clk_get_rate (i2c_clock);    //獲取I2C輸入時鐘批頻率27MHz  
  25.   
  26.     DEB1 ("IP CLOCK = %ld", i2c_davinci_inputClock);  
  27.   
  28.     memset(&i2c_davinci_dev, 0, sizeof(i2c_davinci_dev));  
  29.     init_waitqueue_head(&i2c_davinci_dev.cmd_wait);  
  30.   
  31.     i2c_davinci_dev.regs = (davinci_i2cregsovly)I2C_BASE;      //I2C控制器相關寄存器所在地址  
  32.   
  33.     status = (int)request_region(I2C_BASE, I2C_IOSIZE, MODULE_NAME); //申請一塊IO輸入輸出區域#define MODULE_NAME "DaVinci I2C"  
  34.     if (!status) {  
  35.         i2c_err("I2C is already in use\n");  
  36.         return -ENODEV;  
  37.     }  
  38.   
  39.     status = request_irq(IRQ_I2C, i2c_davinci_isr, 0, "i2c",  
  40.                  &i2c_davinci_dev);  //申請I2C中斷號IRQ_I2C=39  
  41.     if (status) {  
  42.         i2c_err("failed to request I2C IRQ");  
  43.         goto do_release_region;  
  44.     }  
  45.   
  46.     i2c_set_adapdata(&i2c_davinci_adap, &i2c_davinci_dev);  //adap設備的驅動程序私有數據i2c_davinci_adap->dev->driver_data =i2c_davinci_dev  
  47.     status = i2c_add_adapter(&i2c_davinci_adap);    //I2C上添加adapter  
  48.     if (status) {  
  49.         i2c_err("failed to add adapter");  
  50.         goto do_free_irq;  
  51.     }  
  52.   
  53.     i2c_davinci_reset(&i2c_davinci_dev);   //完成I2C相關寄存器的初始化  
  54.   
  55.     if (driver_register(&davinci_i2c_driver) != 0)            // I2C驅動注冊  
  56.         printk(KERN_ERR "Driver register failed for davinci_i2c\n");  
  57.     if (platform_device_register(&davinci_i2c_device) != 0) {  
  58.         printk(KERN_ERR "Device register failed for i2c\n");  
  59.         driver_unregister(&davinci_i2c_driver);  
  60.     }  
  61.   
  62.     return 0;  
  63.   
  64.       do_free_irq:  
  65.     free_irq(IRQ_I2C, &i2c_davinci_dev);  
  66.       do_release_region:  
  67.     release_region(I2C_BASE, I2C_IOSIZE);  
  68.   
  69.     return status;  
  70. }  

操作主要圍繞以下4個結構體展開:

  1. static struct i2c_algorithm i2c_davinci_algo = {            //主要涉及相關的I2C通信協議以及數據的傳輸和接受  
  2.     .name = "DAVINCI I2C algorithm",  
  3.     .id = I2C_ALGO_EXP,  
  4.     .master_xfer = i2c_davinci_xfer,  
  5.     .smbus_xfer = NULL,  
  6.     .slave_send = NULL,  
  7.     .slave_recv = NULL,  
  8.     .algo_control = NULL,  
  9.     .functionality = i2c_davinci_func,  
  10. };  
  11.   
  12. static struct i2c_adapter i2c_davinci_adap = {       //對I2C通信協議的封裝,定義的適配器實例  
  13.     .owner = THIS_MODULE,  
  14.     .name = "DAVINCI I2C adapter",  
  15.     .id = I2C_ALGO_EXP,  
  16.     .algo = &i2c_davinci_algo,  
  17.     .algo_data = NULL,  
  18.     .client_register = NULL,  
  19.     .client_unregister = NULL,  
  20. };  
  21.   
  22. static struct device_driver davinci_i2c_driver = {            //達芬奇的設備驅動  
  23.     .name = "davinci_i2c",  
  24.     .bus = &platform_bus_type,  
  25.     .remove = davinci_i2c_remove,  
  26. };  
  27.   
  28. static struct platform_device davinci_i2c_device = {         //定義的達芬奇平台設備  
  29.     .name = "i2c",  
  30.     .id = -1,  
  31.     .dev = {  
  32.         .driver = &davinci_i2c_driver,  
  33.         .release = davinci_i2c_device_release,  
  34.         },  
  35. };  

結合以上兩段代碼,可以知道以下內容

1 .在模塊初始化函數中完成適配器與設備。的連接了設備驅動davinci_i2c_driver和平台設備davinci_i2c_device的注冊

2.獲取I2C相關端口的申請,I2C相應寄存器的物理首地址0x01C21000,完成了I2C模塊初始化操作以及IRQ_I2C=39的中斷申請。

在這裡對I2C模塊的初始化進行解析,內容如下:

  1. static int i2c_davinci_reset(struct i2c_davinci_device *dev)  
  2. {  
  3.     u16 psc;  
  4.     u32 clk;  
  5.   
  6.         DEB1("i2c: reset called");  
  7.   
  8.     /* put I2C into reset */  
  9.     dev->regs->icmdr &= ~DAVINCI_I2C_ICMDR_IRS_MASK; //首先需復位I2C模塊  
  10.   
  11.         /* NOTE: I2C Clock divider programming info  
  12.      * As per I2C specs the following formulas provide prescalar  
  13.          * and low/high divider values  
  14.      *  
  15.      * input clk --> PSC Div -----------> ICCL/H Div --> output clock  
  16.      *                       module clk  
  17.      *  
  18.      * output clk = module clk / (PSC + 1) [ (ICCL + d) + (ICCH + d) ]  
  19.      *  
  20.      * Thus,  
  21.      * (ICCL + ICCH) = clk = (input clk / ((psc +1) * output clk)) - 2d;  
  22.      *  
  23.      * where if PSC == 0, d = 7,  
  24.      *       if PSC == 1, d = 6  
  25.      *       if PSC > 1 , d = 5  
  26.      */  
  27.   
  28.     psc = 26; /* To get 1MHz clock */  
  29.   
  30.   clk = ((i2c_davinci_inputClock/(psc + 1)) / (i2c_davinci_busFreq * 1000)) - 10; //i2c_davinci_busFreq=20  
  31.   
  32.     dev->regs->icpsc = psc;  //PSC寄存器寫26,即預分頻數27  
  33.     dev->regs->icclkh = (50 * clk) / 100; /* duty cycle should be 50% */  
  34.     dev->regs->icclkl = (clk - dev->regs->icclkh); //高低電平配置內容  
  35.   
  36.     DEB1("CLK  = %d", clk);  
  37.     DEB1("PSC  = %d", dev->regs->icpsc);  
  38.     DEB1("CLKL = %d", dev->regs->icclkl);  
  39.     DEB1("CLKH = %d", dev->regs->icclkh);  
  40.   
  41.     /* Set Own Address: */  
  42.     dev->regs->icoar = i2c_davinci_own_addr; //oxa  
  43.   
  44.     /* Enable interrupts */  
  45.     dev->regs->icimr = I2C_DAVINCI_INTR_ALL;   //所有中斷都使能  
  46.   
  47.     /* Take the I2C module out of reset: */  
  48.     dev->regs->icmdr |= DAVINCI_I2C_ICMDR_IRS_MASK;    //使能I2C模塊  
  49.   
  50.     return 0;  
  51. }  

主要完成Dm6441上I2C的總線時鐘的配置。以及做一些中斷使能的初始化。在這裡都是對dev->regs這個結構體指針的內容

該結構體的實際內容如下:

  1. typedef struct {  
  2.     u16 icoar;  
  3.     u8 rsvd0[2];  
  4.     u16 icimr;  
  5.     u8 rsvd1[2];  
  6.     u16 icstr;  
  7.     u8 rsvd2[2];  
  8.     u16 icclkl;  
  9.     u8 rsvd3[2];  
  10.     u16 icclkh;  
  11.     u8 rsvd4[2];  
  12.     u16 iccnt;  
  13.     u8 rsvd5[2];  
  14.     u16 icdrr;  
  15.     u8 rsvd6[2];  
  16.     u16 icsar;  
  17.     u8 rsvd7[2];  
  18.     u16 icdxr;  
  19.     u8 rsvd8[2];  
  20.     u16 icmdr;  
  21.     u8 rsvd9[2];  
  22.     u16 icivr;  
  23.     u8 rsvd10[2];  
  24.     u16 icemdr;  
  25.     u8 rsvd11[2];  
  26.     u16 icpsc;  
  27.     u8 rsvd12[2];  
  28.     u16 icpid1;  
  29.     u8 rsvd13[2];  
  30.     u16 icpid2;  
  31.     u8 rsvd14[14];  
  32.     u32 ipcfunc;  
  33.     u32 icpdir;  
  34.     u32 icpdin;  
  35.     u32 icpdout;  
  36.     u32 icpdset;  
  37.     u32 icpdclr;  
  38. } davinci_i2cregs;  
  39.   
  40. /**************************************************************************\  
  41. * Overlay structure typedef definition  
  42. \**************************************************************************/  
  43. typedef volatile davinci_i2cregs *davinci_i2cregsovly;  

涉及的內容是DM6441 I2C模塊的相關寄存器。在獲取寄存器的物理首地址之後,就可以直接對這個結構體實例的內容寫數值,該結構體被包含在以下結構體中

  1. struct i2c_davinci_device {  
  2.     int cmd_complete, cmd_err;  
  3.     wait_queue_head_t cmd_wait;  
  4.     u8 *buf;  
  5.     size_t buf_len;  
  6.     davinci_i2cregsovly regs;  
  7. };  

這裡的另一塊內容是添加適配器到I2C驅動中使用i2c_add_adapter函數完成i2c_davinci_adap(該結構體實例內容見上述代碼)

  1. int i2c_add_adapter(struct i2c_adapter *adap)  
  2. {  
  3.     int id, res = 0;  
  4.     struct list_head   *item;  
  5.     struct i2c_driver  *driver;  
  6.   
  7.     down(&core_lists);  
  8.   
  9.     if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0) {  
  10.         res = -ENOMEM;  
  11.         goto out_unlock;  
  12.     }  
  13.   
  14.     res = idr_get_new(&i2c_adapter_idr, NULL, &id);  
  15.     if (res < 0) {  
  16.         if (res == -EAGAIN)  
  17.             res = -ENOMEM;  
  18.         goto out_unlock;  
  19.     }  
  20.   
  21.     adap->nr =  id & MAX_ID_MASK;  
  22.     init_MUTEX(&adap->bus_lock);  
  23.     init_MUTEX(&adap->clist_lock);  
  24.     list_add_tail(&adap->list,&adapters);  
  25.     INIT_LIST_HEAD(&adap->clients);  
  26.   
  27.     /* Add the adapter to the driver core.  
  28.      * If the parent pointer is not set up,  
  29.      * we add this adapter to the host bus.  
  30.      */  
  31.     if (adap->dev.parent == NULL)  
  32.         adap->dev.parent = &platform_bus;  
  33.     sprintf(adap->dev.bus_id, "i2c-%d", adap->nr);  
  34.     adap->dev.driver = &i2c_adapter_driver;   //適配器的設備驅動指向i2c_adapter_driver  
  35.     adap->dev.release = &i2c_adapter_dev_release;  
  36.     device_register(&adap->dev);                       //適配器設備注冊  
  37.     device_create_file(&adap->dev, &dev_attr_name);  
  38.   
  39.     /* Add this adapter to the i2c_adapter class */  
  40.     memset(&adap->class_dev, 0x00, sizeof(struct class_device));  
  41.     adap->class_dev.dev = &adap->dev;  
  42.     adap->class_dev.class = &i2c_adapter_class;  
  43.     strlcpy(adap->class_dev.class_id, adap->dev.bus_id, BUS_ID_SIZE);  
  44.     class_device_register(&adap->class_dev);  
  45.   
  46.     /* inform drivers of new adapters */  
  47.     list_for_each(item,&drivers) {  
  48.         driver = list_entry(item, struct i2c_driver, list);  
  49.         if (driver->flags & I2C_DF_NOTIFY)  
  50.             /* We ignore the return code; if it fails, too bad */  
  51.             driver->attach_adapter(adap);  
  52.     }  
  53.   
  54.     dev_dbg(&adap->dev, "registered as adapter #%d\n", adap->nr);  
  55.   
  56. out_unlock:  
  57.     up(&core_lists);  
  58.     return res;  
  59. }  

在該函數內主要完成適配器結構體中成員變量設備dev的初始化,緊接著完成適配器這個抽象設備的注冊,再完成適配器設備類的注冊以及相關設備節點的生成。

Copyright © Linux教程網 All Rights Reserved