ioremap()函数也是需要建立新的页表,但是不会分配内存.它的功能是将一个物理地址转换成内核需要的虚拟地址(逻辑地址),从而实际硬件的操作.其相反的动作为iounmap().
以S3C2440的RTC驱动为例:
struct platform_device s3c_device_rtc = { .name = "s3c2410-rtc", .id = -1, .num_resources = ARRAY_SIZE(s3c_rtc_resource), .resource = s3c_rtc_resource, };
-->
/* RTC */ static struct resource s3c_rtc_resource[] = { [0] = { .start = S3C24XX_PA_RTC, .end = S3C24XX_PA_RTC + 0xff, .flags = IORESOURCE_MEM, }, [1] = { .start = IRQ_RTC, .end = IRQ_RTC, .flags = IORESOURCE_IRQ, }, [2] = { .start = IRQ_TICK, .end = IRQ_TICK, .flags = IORESOURCE_IRQ } };
-->
#define S3C24XX_PA_RTC S3C2410_PA_RTC
-->
#define S3C2410_PA_RTC (0x57000000)
其中,0x5700 0000便是S3C2440的RTC基地址,这里是物理地址,可参见其数据手册.接下来的程序操作中,便是涉及到此寄存器地址的操作.但是这里是物理地址,是不能直接被内核所操作的,必须将其转换为虚拟地址(逻辑地址).如下:
static struct platform_driver s3c2410_rtc_driver = { .probe = s3c_rtc_probe, .remove = __devexit_p(s3c_rtc_remove), .suspend = s3c_rtc_suspend, .resume = s3c_rtc_resume, .driver = { .name = "s3c2410-rtc", .owner = THIS_MODULE, }, };
-->
static int __devinit s3c_rtc_probe(struct platform_device *pdev) { ... ...; s3c_rtc_base = ioremap(res->start, res->end - res->start + 1); ... ...; }
为了保持可移植性,不应把ioremap返回的地址当作指向内存的指针而直接访问,可以借助内核提供的两个API进行读写:readb()和writeb().以S3C2440-RTC为例:
static int __devinit s3c_rtc_probe(struct platform_device *pdev) { ... ...; s3c_rtc_base = ioremap(res->start, res->end - res->start + 1); ... ...; }
-->
1. static void s3c_rtc_enable(struct platform_device *pdev, int en) 2. { 3. void __iomem *base = s3c_rtc_base; 4. unsigned int tmp; 5. 6. if (s3c_rtc_base == NULL) 7. return; 8. 9. if (!en) { 10. tmp = readb(base + S3C2410_RTCCON); 11. writeb(tmp & ~S3C2410_RTCCON_RTCEN, base + S3C2410_RTCCON); 12. 13. tmp = readb(base + S3C2410_TICNT); 14. writeb(tmp & ~S3C2410_TICNT_ENABLE, base + S3C2410_TICNT); 15. } else { 16. /* re-enable the device, and check it is ok */ 17. 18. if ((readb(base+S3C2410_RTCCON) & S3C2410_RTCCON_RTCEN) == 0){ 19. dev_info(&pdev->dev, "rtc disabled, re-enabling\n"); 20. 21. tmp = readb(base + S3C2410_RTCCON); 22. writeb(tmp|S3C2410_RTCCON_RTCEN, base+S3C2410_RTCCON); 23. } 24. 25. if ((readb(base + S3C2410_RTCCON) & S3C2410_RTCCON_CNTSEL)){ 26. dev_info(&pdev->dev, "removing RTCCON_CNTSEL\n"); 27. 28. tmp = readb(base + S3C2410_RTCCON); 29. writeb(tmp& ~S3C2410_RTCCON_CNTSEL, base+S3C2410_RTCCON); 30. } 31. 32. if ((readb(base + S3C2410_RTCCON) & S3C2410_RTCCON_CLKRST)){ 33. dev_info(&pdev->dev, "removing RTCCON_CLKRST\n"); 34. 35. tmp = readb(base + S3C2410_RTCCON); 36. writeb(tmp & ~S3C2410_RTCCON_CLKRST, base+S3C2410_RTCCON); 37. } 38. } 39. }
小结:
当我们在内核驱动中拿到一个物理地址时,需要将其转换为内核所需要的虚拟地址(逻辑地址),这个动作通过ioremap()来完成,相反动作为iounremap();把物理地址转换为虚拟地址后,为了其可移植性,也通过内核提供的统一的API来实现,分别是writeb()和readb(),当然,这只是针对字节的操作,还有一系列的扩展操作如writel()、readl()等类似.