功能:将IO位址空間映射到核心的虛拟空間上。
linux-2.6.x/arch/arm/io.h
208 /*
209 * ioremap and friends.
210 *
211 * ioremap takes a PCI memory address, as specified in
212 * Documentation/IO-mapping.txt.
213 *
214 */
215 #ifndef __arch_ioremap
216 #define ioremap(cookie,size) __arm_ioremap(cookie, size, MT_DEVICE)
217 #define ioremap_nocache(cookie,size) __arm_ioremap(cookie, size, MT_DEVICE)
218 #define ioremap_cached(cookie,size) __arm_ioremap(cookie, size, MT_DEVICE_CACHED)
219 #define ioremap_wc(cookie,size) __arm_ioremap(cookie, size, MT_DEVICE_WC)
220 #define iounmap(cookie) __iounmap(cookie)
221 #else
222 #define ioremap(cookie,size) __arch_ioremap((cookie), (size), MT_DEVICE)
223 #define ioremap_nocache(cookie,size) __arch_ioremap((cookie), (size), MT_DEVICE)
224 #define ioremap_cached(cookie,size) __arch_ioremap((cookie), (size), MT_DEVICE_CACHED)
225 #define ioremap_wc(cookie,size) __arch_ioremap((cookie), (size), MT_DEVICE_WC)
226 #define iounmap(cookie) __arch_iounmap(cookie)
227 #endif
1. __phys_to_pfn
1.1 函數原型: linux-2.6.x/arch/arm/include/asm/memory.h
1.2 功能:轉換實體位址到一個頁數量,簡單的說就是位址空間/4096,意思就是所給位址是由多少個4096組成。
130 /*
131 * Convert a physical address to a Page Frame Number and back
132 */
133 #define __phys_to_pfn(paddr) ((paddr) >> PAGE_SHIFT)
134 #define __pfn_to_phys(pfn) ((pfn) << PAGE_SHIFT)
2. PAGE_SHIFT:确定一個頁大小
2.1 宏定義Linux/arch/arm/include/asm/page.h
13 /* PAGE_SHIFT determines the page size */
14 #define PAGE_SHIFT 12
15 #define PAGE_SIZE (1UL << PAGE_SHIFT) //4096
16 #define PAGE_MASK (~(PAGE_SIZE-1)) //-4096
由些可得到,頁大小(PAGE_SIZE)為4096,頁掩碼(PAGE_MASK)為-4096
linux-2.6.x/arch/arm/mm/ioremap.c
252 /*
253 * Remap an arbitrary physical address space into the kernel virtual
254 * address space. Needed when the kernel wants to access high addresses
255 * directly.
重映射一個任意實體位址空間到核心虛拟位址空間。核心直接通路高位址
256 *
257 * NOTE! We need to allow non-page-aligned mappings too: we will obviously
258 * have to convert them into an offset in a page-aligned mapping, but the
259 * caller shouldn't need to know that small detail.
需要允許非頁面對齊映射,轉換一個頁對齊映射偏移,但需已知對方是小節。
260 *
261 * 'flags' are the extra L_PTE_ flags that you want to specify for this
262 * mapping. See <asm/pgtable.h> for more information.
‘flags’擴充L_PTE_ 标志特殊的映射。
263 */
264 void __iomem *
265 __arm_ioremap_pfn(unsigned long pfn, unsigned long offset, size_t size,
266 unsigned int mtype)
267 {
268 const struct mem_type *type;
269 int err;
270 unsigned long addr;
271 struct vm_struct * area;
272
273 /*
274 * High mappings must be supersection aligned
275 */
276 if (pfn >= 0x100000 && (__pfn_to_phys(pfn) & ~SUPERSECTION_MASK))
277 return NULL;
278
279 type = get_mem_type(mtype);
280 if (!type)
281 return NULL;
282
283 /*
284 * Page align the mapping size, taking account of any offset.
285 */
286 size = PAGE_ALIGN(offset + size);
/*
linux-2.6.x/include/linux/mm.h
44 to align the pointer to the (next) page boundary
45 #define PAGE_ALIGN(addr) ALIGN(addr, PAGE_SIZE)
linux-2.6.x/include/linkage.h
53 #define ALIGN __ALIGN
linux-2.6.x/arch/arm/include/asm/linkage.h
4 #define __ALIGN .align 0 //預設位元組對齊
*/
287
288 area = get_vm_area(size, VM_IOREMAP);//保留一連續核心虛拟區
/*
get_vm_area - reserve a contiguous kernel virtual area
@size: size of the area
@flags: %VM_IOREMAP for I/O mappings or VM_ALLOC
Search an area of @size in the kernel virtual mapping area,
and reserved it for out purposes. Returns the area descriptor
on success or %NULL on failure.
struct vm_struct *get_vm_area(unsigned long size, unsigned long flags)
{
return __get_vm_area_node(size, flags, VMALLOC_START, VMALLOC_END,
-1, GFP_KERNEL, __builtin_return_address(0));
}
289 if (!area)
290 return NULL;
291 addr = (unsigned long)area->addr;
292
293 #ifndef CONFIG_SMP
294 if (DOMAIN_IO == 0 &&
295 (((cpu_architecture() >= CPU_ARCH_ARMv6) && (get_cr() & CR_XP)) ||
296 cpu_is_xsc3()) && pfn >= 0x100000 &&
297 !((__pfn_to_phys(pfn) | size | addr) & ~SUPERSECTION_MASK)) {
298 area->flags |= VM_ARM_SECTION_MAPPING;
299 err = remap_area_supersections(addr, pfn, size, type);
300 } else if (!((__pfn_to_phys(pfn) | size | addr) & ~PMD_MASK)) {
301 area->flags |= VM_ARM_SECTION_MAPPING;
302 err = remap_area_sections(addr, pfn, size, type);
303 } else
304 #endif
305 err = remap_area_pages(addr, pfn, size, type);
306
307 if (err) {
308 vunmap((void *)addr);
309 return NULL;
310 }
311
312 flush_cache_vmap(addr, addr + size);
313 return (void __iomem *) (offset + addr);
314 }
315 EXPORT_SYMBOL(__arm_ioremap_pfn);
316
317 void __iomem *
318 __arm_ioremap(unsigned long phys_addr, size_t size, unsigned int mtype)
319 {
320 unsigned long last_addr;
321 unsigned long offset = phys_addr & ~PAGE_MASK; //實體位址 & 4096
322 unsigned long pfn = __phys_to_pfn(phys_addr); //實體位址右移12位
323
324 /*
325 * Don't allow wraparound or zero size
326 */
327 last_addr = phys_addr + size - 1;
328 if (!size || last_addr < phys_addr)
329 return NULL;
330