專注電子技術(shù)學(xué)習(xí)與研究
當(dāng)前位置:單片機(jī)教程網(wǎng) >> MCU設(shè)計(jì)實(shí)例 >> 瀏覽文章

混雜字符設(shè)備之LED設(shè)備驅(qū)動(dòng)設(shè)計(jì)及CPU寄存器虛擬內(nèi)存映射分析

作者:龔平   來(lái)源:本站原創(chuàng)   點(diǎn)擊數(shù):  更新時(shí)間:2014年03月14日   【字體:

混雜字符設(shè)備的主要特點(diǎn)是主設(shè)備號(hào)(10)公用,通過(guò)一個(gè)鏈表將各個(gè)設(shè)備關(guān)聯(lián)起來(lái),設(shè)備的識(shí)別主要依靠次設(shè)備號(hào)。

混雜設(shè)備存在自己的結(jié)構(gòu)體:
  1. struct device;

  2.  
  3. struct miscdevice {
     
  4.     int minor;
     
  5.     const char *name;
     
  6.     const struct file_operations *fops;
     
  7.     struct list_head list;
     
  8.     struct device *parent;
  9.     struct device *this_device;
     
  10. };
其中struct device*可以聯(lián)想到自動(dòng)加載設(shè)備文件中的class_create()和device_create()兩個(gè)函數(shù)。因此樂(lè)意推測(cè)混雜字符設(shè)備是自動(dòng)加載設(shè)備文件的設(shè)備驅(qū)動(dòng)。

 
其中主要的兩個(gè)函數(shù)分別是misc_register()和misc_deregister(),分別用來(lái)添加和去除混雜設(shè)備。這兩個(gè)函數(shù)分別在初始化函數(shù)和卸載函數(shù)中調(diào)用。
  1. static struct class *misc_class;
     

  2.  
  3. static const struct file_operations misc_fops = {
     
  4.     .owner        = THIS_MODULE,
     
  5.     .open        = misc_open,
     
  6. };
  7.  
     
  8. int misc_register(struct miscdevice * misc)
     
  9. {
     
  10.     struct miscdevice *c;
     
  11.     dev_t dev;
     
  12.     int err = 0;
     

  13.  
  14.     INIT_LIST_HEAD(&misc->list);
     

  15.  
  16.     mutex_lock(&misc_mtx);
     
  17.     list_for_each_entry(c, &misc_list, list) {
     
  18.         if (c->minor == misc->minor) {
     
  19.             mutex_unlock(&misc_mtx);
     
  20.             return -EBUSY;
     
  21.         }
     
  22.     }
     

  23.  
  24.     if (misc->minor == MISC_DYNAMIC_MINOR) {
     
  25.         int i = DYNAMIC_MINORS;
     
  26.         while (--i >= 0)
     
  27.             if ( (misc_minors[i>>3] & (1 << (i&7))) == 0)
     
  28.                 break;
     
  29.         if (i<0) {
     
  30.             mutex_unlock(&misc_mtx);
     
  31.             return -EBUSY;
     
  32.         }
     
  33.         misc->minor = i;
     
  34.     }
     

  35.  
  36.     if (misc->minor < DYNAMIC_MINORS)
     
  37.         misc_minors[misc->minor >> 3] |= 1 << (misc->minor & 7);
     
  38.     dev = MKDEV(MISC_MAJOR, misc->minor);
     
  39.     /*創(chuàng)建設(shè)備*/
  40.     misc->this_device = device_create(misc_class, misc->parent, dev, NULL,
  41.                      "%s", misc->name);
     
  42.     if (IS_ERR(misc->this_device)) {
     
  43.         err = PTR_ERR(misc->this_device);
     
  44.         goto out;
     
  45.     }
     

  46.  
  47.     list_add(&misc->list, &misc_list);
     
  48.  out:
     
  49.     mutex_unlock(&misc_mtx);
     
  50.     return err;
     
  51. }

int misc_deregister(struct miscdevice *misc)
{
int i = misc->minor;

 
if (list_empty(&misc->list))
return -EINVAL;

 
mutex_lock(&misc_mtx);
list_del(&misc->list);
/*釋放設(shè)備*/
device_destroy(misc_class, MKDEV(MISC_MAJOR, misc->minor));
if (i < DYNAMIC_MINORS && i>0) {
misc_minors[i>>3] &= ~(1 << (misc->minor & 7));
}
mutex_unlock(&misc_mtx);
return 0;
}

 
static int __init misc_init(void)
{
int err;
        ...
/*創(chuàng)建一個(gè)設(shè)備類*/
misc_class = class_create(THIS_MODULE, "misc");
...
return err;
}
從源碼中可以知道混雜字符設(shè)備就是自動(dòng)創(chuàng)建設(shè)備文件的設(shè)備驅(qū)動(dòng)。

 
LED 的字符設(shè)備驅(qū)動(dòng),由于沒(méi)有讀寫操作,只需要完成最控制操作,也就是ioctl函數(shù)的實(shí)現(xiàn)。由于open函數(shù)默認(rèn)情況下就是打開(kāi),所以不去實(shí)現(xiàn)也是可以的。 ioctl函數(shù)的實(shí)現(xiàn)主要包含兩個(gè)步驟,分別是定義命令和實(shí)現(xiàn)命令。其中的定義命令包含類型、方向、數(shù)據(jù)大小、以及命令序號(hào),這些都可以按著一定宏定義實(shí)現(xiàn)。實(shí)現(xiàn)控制也就是ioctl函數(shù)的定義,其中包含,三部分:(1)、命令的檢查,類型和序號(hào);(2)、指針參數(shù)的可讀可寫檢查;(3)具體命令的實(shí)現(xiàn) (switch-case)。
LED的實(shí)現(xiàn)主要就是控制全亮、全滅,某一個(gè)亮,某一個(gè)滅。我的開(kāi)發(fā)板是TQ2440,利用了GPB5-GPB8來(lái)控制4個(gè)LED,只要當(dāng)端口為低電平時(shí),LED亮,高電平時(shí),LED滅。

 
具體的實(shí)現(xiàn)如下:
  1. #include<linux/module.h>
  2. #include<linux/types.h>
     
  3. #include<linux/fs.h>
     
  4. #include<linux/sched.h>
     
  5. #include<linux/init.h>
     
  6. #include<linux/cdev.h>
     
  7. #include<linux/device.h>
     
  8. #include<linux/mm.h>
     
  9. #include<linux/miscdevice.h>
     
  10. /*平臺(tái)相關(guān)的頭文件*/
     
  11. #include<mach/regs-gpio.h>
     
  12. #include<mach/hardware.h>
     
  13. #include<linux/errno.h>
     
  14. #include<linux/gpio.h>
     
  15. #include<linux/cdev.h>
     
  16. #include<linux/slab.h>
     
  17. #include<linux/string.h>
     
  18. #include<linux/kernel.h>
     

  19.  

  20.  
  21. /*定義自己的命令*/
     
  22. /*定義幻數(shù),表示具體的設(shè)備*/
     
  23. #define LED_MAGIC_NUMBER 'k'
     
  24. #define LED_ALL_ON _IO(LED_MAGIC_NUMBER,0)
     
  25. #define LED_ALL_OFF _IO(LED_MAGIC_NUMBER,1)
     
  26. #define LED_ON _IO(LED_MAGIC_NUMBER,2)
     
  27. #define LED_OFF _IO(LED_MAGIC_NUMBER,3)
     
  28. #define LED_MAX_CMD 4
     

  29.  
  30. /*設(shè)備名*/
     
  31. #define DEVICE_NAME "GP_LED"
     
  /*具體的端口號(hào)*/
  1. static unsigned int led_table[] =
     
  2. {
     
  3.         S3C2410_GPB5,
  4.         S3C2410_GPB6,
  5.         S3C2410_GPB7,
  6.         S3C2410_GPB8,
     
  7. };
     
  8. /*端口的功能數(shù)組*/
  9. static unsigned int led_cfg_table[]=
     
  10. {
     
  11.         S3C2410_GPB5_OUTP,
  12.         S3C2410_GPB6_OUTP,
  13.         S3C2410_GPB7_OUTP,
  14.         S3C2410_GPB8_OUTP,
     

  15.         /*或者采用通用功能*/
  16.         /*
  17. S3C2410_GPIO_OUTPUT,
  18.         S3C2410_GPIO_OUTPUT,
  19.         S3C2410_GPIO_OUTPUT,
  20.         S3C2410_GPIO_OUTPUT,
  21. */
  22. };
     

  23.  
  24. static int s3c2440_led_ioctl(
     
  25.         struct inode * inode,
     
  26.         struct file *file,
     
  27.         unsigned int cmd,
     
  28.         unsigned long arg
     
  29. )
     
  30. {
     
  31.         int i = 0;
     
  32.         /*檢測(cè)參數(shù)的正確性*/
  33.         if(_IOC_TYPE(cmd)!=LED_MAGIC_NUMBER)
  34.                 return -EINVAL;
  35.         /*檢查命令是否超過(guò)一定的界限*/
  36.         if(_IOC_NR(cmd) >= LED_MAX_CMD)
  37.                 return -EINVAL;

  38.         /*檢查arg參數(shù)的正確性*/
  39.         if(arg<0 || arg >4)
  40.         {
  41.                 return -EINVAL;
  42.         }
     
  43.         /*命令控制語(yǔ)句*/
  44.         switch(cmd)
     
  45.         {
     
  46.                 case LED_ALL_ON:
     
  47.                 {
     
  48.                         for(i = 0; i < 4; ++ i)
     
  49.                                 s3c2410_gpio_setpin(led_table[arg-i-1],0);
     
  50.                         break;
     
  51.                 }
     
  52.                 case LED_ALL_OFF:
     
  53.                 {
     
  54.                         for(i = 0; i < 4; ++ i)
     
  55.                                 s3c2410_gpio_setpin(led_table[arg-i-1],1);
     
  56.                         break;
     
  57.                 }
     
  58.                 case LED_ON:
     
  59.                 {
     
  60.                         s3c2410_gpio_setpin(led_table[arg],0);
     
  61.                         break;
     
  62.                 }
     
  63.                 case LED_OFF:
     
  64.                 {
     
  65.                         s3c2410_gpio_setpin(led_table[arg],1);
     
  66.                         break;
     
  67.                 }
     
  68.                 default:
     
  69.                 {
     
  70.                         return -EINVAL;
     
  71.                         break;
     
  72.                 }
     
  73.         }
     

  74.  

  75.  
  76.         return 0;
     
  77. }
     

  78.  
  79. /*具體函數(shù)*/
     
  80. static const struct file_operations led_fops =
     
  81. {
     
  82.         .owner = THIS_MODULE,
     
  83.         .ioctl = s3c2440_led_ioctl,
     
  84. };
     

  85.  
  86. /*混雜設(shè)備類*/
     
  87. static const struct miscdevice misc =
     
  88. {
     
  89.         .minor = MISC_DYNAMIC_MINOR,
     
  90.         .name = DEVICE_NAME,
     
  91.         /*此處是一個(gè)地址,而不是一個(gè)數(shù)*/
     
  92.         .fops = &led_fops,
     
  93. };
     

  94.  
  95. /*初始化*/
     
  96. static int __init dev_init(void)
     
  97. {
     
  98.         int ret;
     

  99.  
  100.         int i;
     

  101.  
  102.         for(i = 0; i<4; ++i)
     
  103.         {
     
  104.                 s3c2410_gpio_cfgpin(led_table[i],led_cfg_table[i]);
  105.                 s3c2410_gpio_setpin(led_table[i],1);
     
  106.         }
     

  107.  
  108.         ret = misc_register(&misc);
     
  109.         printk(DEVICE_NAME"\tinitialized\n");
     

  110.  
  111.         return ret;
     
  112. }
     

  113.  
  114. /*退出*/
     
  115. static void __exit dev_exit(void)
     
  116. {
     
  117.         misc_deregister(&misc);
     
  118. }
     

  119.  
  120. module_init(dev_init);
     
  121. module_exit(dev_exit);
     

  122.  
  123. MODULE_LICENSE("GPL");
     
  124. MODULE_AUTHOR("GP");
測(cè)試應(yīng)用程序如下:
  1. #include<stdio.h>
  2. #include<stdlib.h>
     
  3. #include<unistd.h>
     
  4. #include<fcntl.h>
     
  5. #include<sys/ioctl.h>
     

  6.  
  7. #define LED_MAGIC_NUMBER 'k'
     
  8. #define LED_ALL_ON _IO(LED_MAGIC_NUMBER,0)
     
  9. #define LED_ALL_OFF _IO(LED_MAGIC_NUMBER,1)
     
  10. #define LED_ON _IO(LED_MAGIC_NUMBER,2)
     
  11. #define LED_OFF _IO(LED_MAGIC_NUMBER,3)
     

  12.  
  13. int main(int argc,char *argv[])
     
  14. {
     
  15.         int fd,cmd;
     
  16.         unsigned int arg;
     
  17.         if(argc != 3)
     
  18.         {
     
  19.                 printf("parameter is not right");
     
  20.                 exit(-1);
     
  21.         }
     

  22.  
  23.         cmd = atoi(argv[1]);
     
  24.         arg = atoi(argv[2]);
     

  25.  
  26.         if(cmd > 3 || cmd < 0 || arg > 4 || arg < 0)
     
  27.         {
     
  28.                 printf("The style of command is not right\n");
     
  29.                 exit(-1);
     
  30.         }
     

  31.  
  32.         fd = open("/dev/GP_LED",O_RDWR);
     

  33.  
  34.         if(fd == -1)
     
  35.         {
     
  36.                 printf("Open File wrong!!\n");
     
  37.                 exit(-1);
     
  38.         }
     
  39.         switch(cmd)
     
  40.         {
     
  41.                 case 0:
     
  42.                         cmd = LED_ALL_ON;
     
  43.                         arg = 4;
     
  44.                         break;
     
  45.                 case 1:
     
  46.                         cmd = LED_ALL_OFF;
     
  47.                         arg = 4;
     
  48.                         break;
     
  49.                 case 2:
     
  50.                         cmd = LED_ON;
     
  51.                         break;
     
  52.                 case 3:
     
  53.                         cmd = LED_OFF;
     
  54.                         break;
     
  55.                 default:
     
  56.                         exit(-1);
     
  57.         }
     
  58.         int isOk = ioctl(fd,cmd,arg);
     
  59.         printf("%d",isOk);
     

  60.  
  61.         close(fd);
     

  62.  
  63.         exit(0);
     
  64. }
分析代碼:
應(yīng)用程序沒(méi)什么好分析的,關(guān)鍵是驅(qū)動(dòng)代碼中的幾個(gè)重要的數(shù)據(jù)結(jié)構(gòu)S3C2410_GPB5-S3C2410_GPB8以及 S3C2410_GPB5_OUTP--S3C2410_GPB8_OUTP和兩個(gè)函數(shù) s3c2410_gpio_cfgpin(),s3c2410_gpio_setpin()。
其中3C2410_GPB5- S3C2410_GPB8是指GPB5-GPB8這四個(gè)IO口,Linux中對(duì)端口都進(jìn)行了編號(hào),給予每一個(gè)IO口唯一的端口號(hào)。同時(shí)又將端口分成了很多塊GPx,包括GPA,GPB,...,GPH等。每一塊的起始端口號(hào)為(x-1)*32+0,也就是GPA的起始端口號(hào)為0,而GPB的起始端口號(hào)為 32,依此類推。而S3C2410_GPB5_OUTP是指將GPB5配置為輸出口,每一個(gè)IO口都是多功能IO,使用前都需要進(jìn)行配置。

 
具體的源碼如下:
  1. ...
  2. /*得到端口號(hào),每一個(gè)IO口的端口號(hào)是唯一的*/
  3. #define S3C2410_GPB5 S3C2410_GPIONO(S3C2410_GPIO_BANKB, 5)
     
  4. /*定義端口的功能,其中的<<10,是因?yàn)樵贕PBCON的第10bit開(kāi)始是配置端口B的功能,其他的也類似,只是位不同*/
  5. #define S3C2410_GPB5_INP (0x00 << 10)
     
  6. #define S3C2410_GPB5_OUTP (0x01 << 10)
     
  7. #define S3C2410_GPB5_nXBACK (0x02 << 10)
     
  8. #define S3C2443_GPB5_XBACK (0x03 << 10)
     
  9. #define S3C2400_GPB5_DATA21 (0x02 << 10)
     
  10. #define S3C2400_GPB5_nCTS1 (0x03 << 10)
     

  11.  
  12. #define S3C2410_GPB6 S3C2410_GPIONO(S3C2410_GPIO_BANKB, 6)
     
  13. #define S3C2410_GPB6_INP (0x00 << 12)
     
  14. #define S3C2410_GPB6_OUTP (0x01 << 12)
     
  15. #define S3C2410_GPB6_nXBREQ (0x02 << 12)
     
  16. #define S3C2443_GPB6_XBREQ (0x03 << 12)
     
  17. #define S3C2400_GPB6_DATA22 (0x02 << 12)
     
  18. #define S3C2400_GPB6_nRTS1 (0x03 << 12)
     

  19.  
  20. #define S3C2410_GPB7 S3C2410_GPIONO(S3C2410_GPIO_BANKB, 7)
     
  21. #define S3C2410_GPB7_INP (0x00 << 14)
     
  22. #define S3C2410_GPB7_OUTP (0x01 << 14)
     
  23. #define S3C2410_GPB7_nXDACK1 (0x02 << 14)
     
  24. #define S3C2443_GPB7_XDACK1 (0x03 << 14)
     
  25. #define S3C2400_GPB7_DATA23 (0x02 << 14)
     

  26.  
  27. #define S3C2410_GPB8 S3C2410_GPIONO(S3C2410_GPIO_BANKB, 8)
     
  28. #define S3C2410_GPB8_INP (0x00 << 16)
     
  29. #define S3C2410_GPB8_OUTP (0x01 << 16)
     
  30. #define S3C2410_GPB8_nXDREQ1 (0x02 << 16)
     
  31. #define S3C2400_GPB8_DATA24 (0x02 << 16)
     
  32. ...
 ...
  1. #define S3C2410_GPIONO(bank,offset) ((bank) + (offset))
     
  2. /*將IO分成8塊,便于管理同一類型的端口*/
  3. #define S3C2410_GPIO_BANKA (32*0)
  4. #define S3C2410_GPIO_BANKB (32*1)
  5. #define S3C2410_GPIO_BANKC (32*2)
  6. #define S3C2410_GPIO_BANKD (32*3)
  7. #define S3C2410_GPIO_BANKE (32*4)
  8. #define S3C2410_GPIO_BANKF (32*5)
  9. #define S3C2410_GPIO_BANKG (32*6)
  10. #define S3C2410_GPIO_BANKH (32*7)
兩個(gè)函數(shù)s3c2410_gpio_cfgpin(),s3c2410_gpio_setpin()分別表示配置端口(配置功能寄存器)和設(shè)置端口(寫讀數(shù)據(jù)寄存器)。

 
在linux內(nèi)核中,通常將將CPU和外設(shè)的寄存器從物理地址靜態(tài)的映射到了虛擬地址空間中以固定地址開(kāi)始的一段內(nèi)存空間上。
S3C24XXCPU的CPU和外設(shè)寄存器映射關(guān)系分布如下圖所示:
具體的實(shí)現(xiàn)參看源碼:
  1. /*配置端口的功能寄存器*/
  2. void s3c2410_gpio_cfgpin(unsigned int pin, unsigned int function)
     
  3. {
     
  4.     void __iomem *base = S3C24XX_GPIO_BASE(pin);
     
  5.     /*mask可以用來(lái)清零,或者置位*/
  6.     unsigned long mask;
     
  7.     unsigned long con;
     
  8.     unsigned long flags;
     
  9.     
  10.     /*針對(duì)GPA的偏移量,因?yàn)橹挥袃煞N功能,所以只有1bit表示,偏移量也只需要1*/  
  11.     if (pin < S3C2410_GPIO_BANKB) {
     
  12. /*用于將當(dāng)前端口所在的位置位或者清零
  13.   清零:con &= ~mask;
  14.           置位:con |= mask;
  15. */
  16.         mask = 1 << S3C2410_GPIO_OFFSET(pin);
     
  17.     } 
  18.     /*針對(duì)GPB開(kāi)始的端口,因?yàn)楣δ鼙容^多,需要兩個(gè)bit描述一個(gè)端口的功能,所以偏移量乘以2*/
  19.     else {
     
  20. /*同樣也可以實(shí)現(xiàn)將對(duì)應(yīng)的兩個(gè)位置位或者清零*/
  21.         mask = 3 << S3C2410_GPIO_OFFSET(pin)*2;
     
  22.     }
     

  23.  
  24.     /*根據(jù)功能設(shè)置相應(yīng)的設(shè)置相應(yīng)的位操作,這一部分主要是為了實(shí)現(xiàn)對(duì)常規(guī)命令解析*/
     
  25.     switch (function) {
     
  26.     case S3C2410_GPIO_LEAVE:
     
  27.         mask = 0;
     
  28.         function = 0;
     
  29.         break;
     

  30.  
  31.     /*采用通用命令的形式,需要對(duì)相應(yīng)的位進(jìn)行解析,因此驅(qū)動(dòng)中也可以將S3C2410_GPB5_OUTP置換為S3C2410_GPIO_OUTPUT*/
     
  32.     case S3C2410_GPIO_INPUT:
     
  33.     case S3C2410_GPIO_OUTPUT:
     
  34.     case S3C2410_GPIO_SFN2:
     
  35.     case S3C2410_GPIO_SFN3:
     
  36.  
  37.         /*從通用方法中提取對(duì)應(yīng)端口的功能*/
  38.         if (pin < S3C2410_GPIO_BANKB) {
     
  39.             function -= 1;
     
  40.             function &= 1;
     
  41.             function <<= S3C2410_GPIO_OFFSET(pin);
     
  42.         } else {
     
  43.             /*根據(jù)function確定2個(gè)bits的值*/
     
  44.             function &= 3;
     
  45.             /*將function設(shè)置到相應(yīng)的位置,此處是簡(jiǎn)單的位操作*/
     
  46.             function <<= S3C2410_GPIO_OFFSET(pin)*2;
     
  47.         }
     
  48.     }
     

  49.  
  50.     /* modify the specified register wwith IRQs off */
     
     /*寫操作都是先讀再寫*/
  1.     /*保存中斷*/
     
  2.     local_irq_save(flags);
     

  3.  
  4.     /*讀寄存器,base是寄存器基地址,而0x0是表示第一個(gè)寄存器GPBCON*/
     
  5.     con = __raw_readl(base + 0x00);
     
  6.     /*清零對(duì)應(yīng)的位*/
     
  7.     con &= ~mask;
     
  8.     /*設(shè)置相應(yīng)的位為對(duì)應(yīng)的功能*/
     
  9.     con |= function;
     

  10.  
  11.     /*寫入寄存器*/
     
  12.     __raw_writel(con, base + 0x00);
     

  13.  
  14.     /*恢復(fù)中斷*/
     
  15.     local_irq_restore(flags);
     
  16. }
     
 /*寫端口的數(shù)據(jù)寄存器,也就是GPxDAT的某一端口*/
  1. void s3c2410_gpio_setpin(unsigned int pin, unsigned int to)
     
  2. {
     
  3.     /*得到端口所在塊的寄存器基地址*/
     
  4.     void __iomem *base = S3C24XX_GPIO_BASE(pin);
     
  5.     /*得到端口在所在寄存器中具體的偏移量*/
     
  6.     unsigned long offs = S3C2410_GPIO_OFFSET(pin);
     
  7.     unsigned long flags;
     
  8.     unsigned long dat;
     

  9.     /*中斷保存*/
  10.     local_irq_save(flags);
     
  11.     /*讀寄存器GPxDAT,base是所在塊寄存器的基地址,0x4是當(dāng)前寄存器的偏移量*/
  12.     dat = __raw_readl(base + 0x04);
     
  13.     /*清零該端口當(dāng)前的數(shù)據(jù)*/
  14.     dat &= ~(1 << offs);
     
  15.     /*一般是保存在另一個(gè)值中*/
  16.     dat |= to << offs;
     
  17.     __raw_writel(dat, base + 0x04);
     

  18.  
  19.     local_irq_restore(flags);
     
  20. }
     

  1. /*關(guān)于推到地址的方法按照上面的分布圖對(duì)照分析可能比較方便*/
  2. #ifdef CONFIG_CPU_S3C2400
     
  3. #define S3C24XX_GPIO_BASE(x) S3C2400_GPIO_BASE(x)
     
  4. #define S3C24XX_MISCCR S3C2400_MISCCR
     
  5. #else
     
  6. #define S3C24XX_GPIO_BASE(x) S3C2410_GPIO_BASE(x)
     
  7. #define S3C24XX_MISCCR     S3C24XX_GPIOREG2(0x80)
     
  8. #endif
     

  9.  
  10. #define S3C2410_GPIO_BASE(pin) ((((pin) & ~31) >> 1) + S3C24XX_VA_GPIO)
     
  11. #define S3C2410_GPIO_OFFSET(pin) ((pin) & 31)
     

  12.  
  13. /*GPIO的物理地址起始*/
     
  14. #define S3C2410_PA_GPIO     (0x56000000)
     
  15. /*GPIO的虛擬地址起始,實(shí)現(xiàn)的方法是在兩個(gè)物理地間隔加上UART 虛擬地址的起始*/
     
  16. #define S3C24XX_VA_GPIO     ((S3C24XX_PA_GPIO - S3C24XX_PA_UART) + S3C24XX_VA_UART)
     
  17. /*大小*/
     
  18. #define S3C24XX_SZ_GPIO     SZ_1M
     

  19.  

  20.  
  21. #define S3C24XX_VA_UART     S3C_VA_UART
     
  22. #define S3C2410_PA_UART     (0x50000000)
     
  23. #define S3C24XX_SZ_UART     SZ_1M
     
 
 /*映射的虛擬地址的固定起始地址*/ 
 #define S3C_ADDR_BASE (0xF4000000)      
  1. #ifndef __ASSEMBLY__
     
  2. #define S3C_ADDR(x)    ((void __iomem __force *)S3C_ADDR_BASE + (x))
     
  3. #else
     
  4. #define S3C_ADDR(x)    (S3C_ADDR_BASE + (x))
     
  5. #endif
     

  6.  
  7. #define S3C_VA_IRQ    S3C_ADDR(0x00000000)    /* irq controller(s) */
     
  8. #define S3C_VA_SYS    S3C_ADDR(0x00100000)    /* system control */
     
  9. #define S3C_VA_MEM    S3C_ADDR(0x00200000)    /* system control */
     
  10. #define S3C_VA_TIMER    S3C_ADDR(0x00300000)    /* timer block */
     
  11. #define S3C_VA_WATCHDOG    S3C_ADDR(0x00400000)    /* watchdog */
     
  12. #define S3C_VA_UART    S3C_ADDR(0x01000000)    /* UART */
     

  13.  

  14.  
  15. static unsigned int __raw_readl(unsigned int ptr)
     
  16. {
     
  17.     /*volatile表示ptr中的值是易變性的,*((volatile unsigned int *)ptr)是對(duì)地址取值*/
  18.     return *((volatile unsigned int *)ptr);
     
  19. }
     

  20.  

  21.  
  22. static void __raw_writel(unsigned int value, unsigned int ptr)
     
  23. {
     
  24.     /*((volatile unsigned int *)ptr)是對(duì)地址賦值*/
  25.     *((volatile unsigned int *)ptr) = value;
     
  26. }
映射虛擬地址的關(guān)系建議自己繪圖,可能更加的直觀,快捷。在這段代碼中的兩個(gè)宏語(yǔ)句是比較難以理解的。這兩句宏定義充分利用了位操作的優(yōu)勢(shì)。

 
/*求端口所在塊寄存器的起始虛擬地址*/
#define S3C2410_GPIO_BASE(pin)   ((((pin) & ~31) >> 1) + S3C24XX_VA_GPIO)
/*求端口在所在塊的偏移量*/
#define S3C2410_GPIO_OFFSET(pin) ((pin) & 31) 
我們加以分析。我們以GPB5、GPA5作為分析對(duì)象。
GPB5的端口ID號(hào)是32*(2-1)+5=37,GPA5的端口ID號(hào)是32*(1-1)+5=5;
根據(jù)上面的圖可知,S3C24XX_VA_GPIO的值為0xFB000000.
S3C2410_GPIO_BASE(GPB5) = ((37&~31)>>1)+0xFB000000=0xFB000010  (剛好對(duì)應(yīng)于GPBCON的虛擬地址)
S3C2410_GPIO_BASE(GPA5) = ((5&~31)>>1)+0xFB000000=0xFB000000 (剛好對(duì)應(yīng)于GPBACON的虛擬地址)
也就是相當(dāng)于得到每一塊IO口的基地址也就是GPA,GPB,GPC,...等對(duì)應(yīng)的地址。
S3C2410_GPIO_OFFSET(GPB5) = 37 & 31 = 5;相當(dāng)于求32的余數(shù),也就是得到端口在所在塊中的偏移量。

 

 
LED的驅(qū)動(dòng)很簡(jiǎn)單,該驅(qū)動(dòng)是按著ioctl實(shí)現(xiàn)的一般步驟實(shí)現(xiàn)的,具有延續(xù)性。
關(guān)閉窗口

相關(guān)文章