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()等類似.