又一篇講老版本核心驅動的,多年沒寫過核心驅動了,也不知這些技術現在還适用不,權當紀念吧。
Original address:http://blog.chinaunix.net/uid-26009923-id-3291183.html
TQ2440的watchdog linux驅動在核心源碼linux-2.6.30.4的:./drivers/watchdog/s3c2410_wdt.c下
- watchdog ioremap的過程:
res->start=0x53000000 //實體位址
wdt_base=0xc5400000 //虛拟位址
wdt_base = ioremap(res->start, size);
将實體位址res->start映射到了虛拟位址0xc5400000處,映射大小為:size=0x100000.
注: 不知道為什麼剛開始這個size=8, 後來就成了0x100000(1M)。
- arch/arm/include/asm/io.h中, 找到ioremap的定義:
//出現兩個ioremap
#ifndef __arch_ioremap
#define ioremap(cookie,size) __arm_ioremap(cookie, size, MT_DEVICE)
#define ioremap_nocache(cookie,size) __arm_ioremap(cookie, size, MT_DEVICE)
#define ioremap_cached(cookie,size) __arm_ioremap(cookie, size, MT_DEVICE_CACHED)
#define ioremap_wc(cookie,size) __arm_ioremap(cookie, size, MT_DEVICE_WC)
#define iounmap(cookie) __iounmap(cookie)
#else
#define ioremap(cookie,size) __arch_ioremap((cookie), (size), MT_DEVICE)
#define ioremap_nocache(cookie,size) __arch_ioremap((cookie), (size), MT_DEVICE)
#define ioremap_cached(cookie,size) __arch_ioremap((cookie), (size), MT_DEVICE_CACHED)
#define ioremap_wc(cookie,size) __arch_ioremap((cookie), (size), MT_DEVICE_WC)
#define iounmap(cookie) __arch_iounmap(cookie)
#endif
// 沒有定義 #ifndef __arch_ioremap,是以是
#define MT_DEVICE 0
#define ioremap(cookie,size) __arm_ioremap(cookie, size, MT_DEVICE)
- 在arch/arm/mm/ioremap.c中
/**
__arm_ioremap(cookie, size, MT_DEVICE)也有兩個定義,
一個是在nommu.c中,
一個是在arch/arm/mm/ioremap.c中,是以很明顯進入arch/arm/mm/ioremap.c中:
*/
//這個函數将實體位址phys_addr拆成兩部分高20位的頁幀号(Page Frame Number)pfn和低12位的頁内偏移位址offset
void __iomem *
__arm_ioremap(unsigned long phys_addr, size_t size, unsigned int mtype)
{
unsigned long last_addr;
unsigned long offset = phys_addr & ~PAGE_MASK;
unsigned long pfn = __phys_to_pfn(phys_addr);
/*
* Don't allow wraparound or zero size
*/
last_addr = phys_addr + size - 1;
if (!size || last_addr < phys_addr)
return NULL;
//此處列印值如下: last_addr=0x530fffff, size=0x100000, offset=0x0, pfn=0x53000
return __arm_ioremap_pfn(pfn, offset, size, mtype);
}
EXPORT_SYMBOL(__arm_ioremap);
/*
* Convert a physical address to a Page Frame Number and back
*/
#define PAGE_SHIFT 12
#define __phys_to_pfn(paddr) ((paddr) >> PAGE_SHIFT)
- 在arch/arm/mm/ioremap.c中
/*
* Remap an arbitrary physical address space into the kernel virtual
* address space. Needed when the kernel wants to access high addresses
* directly.
*
* We need to allow non-page-aligned mappings too: we will obviously
* have to convert them into an offset in a page-aligned mapping, but the
* caller shouldn't need to know that small detail.
*
* 'flags' are the extra L_PTE_ flags that you want to specify for this
* mapping. See <asm/pgtable.h> for more information.
*/
void __iomem *
__arm_ioremap_pfn(unsigned long pfn, unsigned long offset, size_t size,
unsigned int mtype)
{
const struct mem_type *type;
int err;
unsigned long addr;
struct vm_struct * area;
/*
* High mappings must be supersection aligned
*/
if (pfn >= 0x100000 && (__pfn_to_phys(pfn) & ~SUPERSECTION_MASK))
return NULL;
type = get_mem_type(mtype);
if (!type)
return NULL;
/*
* Page align the mapping size, taking account of any offset.
*/
size = PAGE_ALIGN(offset + size);
area = get_vm_area(size, VM_IOREMAP); //配置設定虛拟位址空間
if (!area)
return NULL;
addr = (unsigned long)area->addr;
//此處列印出的位址: addr=0xc5400000
#ifndef CONFIG_SMP //不進入此處
if (DOMAIN_IO == 0 &&
(((cpu_architecture() >= CPU_ARCH_ARMv6) && (get_cr() & CR_XP)) ||
cpu_is_xsc3()) && pfn >= 0x100000 &&
!((__pfn_to_phys(pfn) | size | addr) & ~SUPERSECTION_MASK)) {
area->flags |= VM_ARM_SECTION_MAPPING;
err = remap_area_supersections(addr, pfn, size, type);
} else if (!((__pfn_to_phys(pfn) | size | addr) & ~PMD_MASK)) {
area->flags |= VM_ARM_SECTION_MAPPING;
err = remap_area_sections(addr, pfn, size, type);
} else
#endif
err = remap_area_pages(addr, pfn, size, type);
if (err) {
vunmap((void *)addr);
return NULL;
}
flush_cache_vmap(addr, addr + size);
return (void __iomem *) (offset + addr);
}
EXPORT_SYMBOL(__arm_ioremap_pfn);
area = get_vm_area(size, VM_IOREMAP); //配置設定虛拟位址空間
err = remap_area_pages(addr, pfn, size, type);
這兩個函數。
- 在arch/arm/mm/ioremap.c中
static int
remap_area_sections(unsigned long virt, unsigned long pfn,
size_t size, const struct mem_type *type)
{
unsigned long addr = virt, end = virt + size;
pgd_t *pgd;
/*
* Remove and free any PTE-based mapping, and
* sync the current kernel mapping.
*/
unmap_area_sections(virt, size);
pgd = pgd_offset_k(addr);
do {
pmd_t *pmd = pmd_offset(pgd, addr);
pmd[0] = __pmd(__pfn_to_phys(pfn) | type->prot_sect);
pfn += SZ_1M >> PAGE_SHIFT;
pmd[1] = __pmd(__pfn_to_phys(pfn) | type->prot_sect);
pfn += SZ_1M >> PAGE_SHIFT;
flush_pmd_entry(pmd);
addr += PGDIR_SIZE;
pgd++;
} while (addr < end);
return 0;
}