最后编辑于: 2016-05-28 00:41 | 分类: linux | 标签: 驱动 | 浏览数: 614 | 评论数: 0
又一篇讲老版本内核驱动的,多年没写过内核驱动了,也不知这些技术现在还适用不,权当纪念吧。
Original address:http://blog.chinaunix.net/uid-26009923-id-3291183.html
TQ2440的watchdog linux驱动在内核源码linux-2.6.30.4的:./drivers/watchdog/s3c2410_wdt.c
下
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;
}