歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> Linux下DM644x設備驅動I2C之總線驅動

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

日期:2017/3/1 10:17:52   编辑:Linux編程
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. DEB0("%s %s", __TIME__, __DATE__);
  6. DEB1("i2c_davinci_init()");
  7. #if 0
  8. if (i2c_davinci_busFreq > 200)
  9. i2c_davinci_busFreq = 400; /*Fast mode */
  10. else
  11. i2c_davinci_busFreq = 100; /*Standard mode */
  12. #endif
  13. i2c_clock = clk_get (dev, "I2CCLK"); //獲取I2C輸入時鐘批頻率27MHz
  14. if (IS_ERR(i2c_clock))
  15. return -1;
  16. clk_use (i2c_clock);
  17. clk_enable (i2c_clock);
  18. i2c_davinci_inputClock = clk_get_rate (i2c_clock); //獲取I2C輸入時鐘批頻率27MHz
  19. DEB1 ("IP CLOCK = %ld", i2c_davinci_inputClock);
  20. memset(&i2c_davinci_dev, 0, sizeof(i2c_davinci_dev));
  21. init_waitqueue_head(&i2c_davinci_dev.cmd_wait);
  22. i2c_davinci_dev.regs = (davinci_i2cregsovly)I2C_BASE; //I2C控制器相關寄存器所在地址
  23. status = (int)request_region(I2C_BASE, I2C_IOSIZE, MODULE_NAME); //申請一塊IO輸入輸出區域#define MODULE_NAME "DaVinci I2C"
  24. if (!status) {
  25. i2c_err("I2C is already in use\n");
  26. return -ENODEV;
  27. }
  28. status = request_irq(IRQ_I2C, i2c_davinci_isr, 0, "i2c",
  29. &i2c_davinci_dev); //申請I2C中斷號IRQ_I2C=39
  30. if (status) {
  31. i2c_err("failed to request I2C IRQ");
  32. goto do_release_region;
  33. }
  34. i2c_set_adapdata(&i2c_davinci_adap, &i2c_davinci_dev); //adap設備的驅動程序私有數據i2c_davinci_adap->dev->driver_data =i2c_davinci_dev
  35. status = i2c_add_adapter(&i2c_davinci_adap); //I2C上添加adapter
  36. if (status) {
  37. i2c_err("failed to add adapter");
  38. goto do_free_irq;
  39. }
  40. i2c_davinci_reset(&i2c_davinci_dev); //完成I2C相關寄存器的初始化
  41. if (driver_register(&davinci_i2c_driver) != 0) // I2C驅動注冊
  42. printk(KERN_ERR "Driver register failed for davinci_i2c\n");
  43. if (platform_device_register(&davinci_i2c_device) != 0) {
  44. printk(KERN_ERR "Device register failed for i2c\n");
  45. driver_unregister(&davinci_i2c_driver);
  46. }
  47. return 0;
  48. do_free_irq:
  49. free_irq(IRQ_I2C, &i2c_davinci_dev);
  50. do_release_region:
  51. release_region(I2C_BASE, I2C_IOSIZE);
  52. return status;
  53. }

操作主要圍繞以下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. static struct i2c_adapter i2c_davinci_adap = { //對I2C通信協議的封裝,定義的適配器實例
  12. .owner = THIS_MODULE,
  13. .name = "DAVINCI I2C adapter",
  14. .id = I2C_ALGO_EXP,
  15. .algo = &i2c_davinci_algo,
  16. .algo_data = NULL,
  17. .client_register = NULL,
  18. .client_unregister = NULL,
  19. };
  20. static struct device_driver davinci_i2c_driver = { //達芬奇的設備驅動
  21. .name = "davinci_i2c",
  22. .bus = &platform_bus_type,
  23. .remove = davinci_i2c_remove,
  24. };
  25. static struct platform_device davinci_i2c_device = { //定義的達芬奇平台設備
  26. .name = "i2c",
  27. .id = -1,
  28. .dev = {
  29. .driver = &davinci_i2c_driver,
  30. .release = davinci_i2c_device_release,
  31. },
  32. };

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

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

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

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

Copyright © Linux教程網 All Rights Reserved