blob: 30f82fb5918c9e2a8b3cce49849ff4fd7d97c2dc [file] [log] [blame]
Nicolas Pitred73cd422008-09-15 16:44:55 -04001/*
2 * arch/arm/mm/highmem.c -- ARM highmem support
3 *
4 * Author: Nicolas Pitre
5 * Created: september 8, 2008
6 * Copyright: Marvell Semiconductors Inc.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 */
12
13#include <linux/module.h>
14#include <linux/highmem.h>
15#include <linux/interrupt.h>
16#include <asm/fixmap.h>
17#include <asm/cacheflush.h>
18#include <asm/tlbflush.h>
19#include "mm.h"
20
21void *kmap(struct page *page)
22{
23 might_sleep();
24 if (!PageHighMem(page))
25 return page_address(page);
26 return kmap_high(page);
27}
28EXPORT_SYMBOL(kmap);
29
30void kunmap(struct page *page)
31{
32 BUG_ON(in_interrupt());
33 if (!PageHighMem(page))
34 return;
35 kunmap_high(page);
36}
37EXPORT_SYMBOL(kunmap);
38
39void *kmap_atomic(struct page *page, enum km_type type)
40{
41 unsigned int idx;
42 unsigned long vaddr;
Nicolas Pitre7929eb92009-09-03 21:45:59 +010043 void *kmap;
Nicolas Pitred73cd422008-09-15 16:44:55 -040044
45 pagefault_disable();
46 if (!PageHighMem(page))
47 return page_address(page);
48
Russell King6a5e2932009-10-11 16:29:48 +010049 debug_kmap_atomic(type);
50
Nicolas Pitre7929eb92009-09-03 21:45:59 +010051 kmap = kmap_high_get(page);
52 if (kmap)
53 return kmap;
54
Nicolas Pitred73cd422008-09-15 16:44:55 -040055 idx = type + KM_TYPE_NR * smp_processor_id();
56 vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx);
57#ifdef CONFIG_DEBUG_HIGHMEM
58 /*
59 * With debugging enabled, kunmap_atomic forces that entry to 0.
60 * Make sure it was indeed properly unmapped.
61 */
62 BUG_ON(!pte_none(*(TOP_PTE(vaddr))));
63#endif
64 set_pte_ext(TOP_PTE(vaddr), mk_pte(page, kmap_prot), 0);
65 /*
66 * When debugging is off, kunmap_atomic leaves the previous mapping
67 * in place, so this TLB flush ensures the TLB is updated with the
68 * new mapping.
69 */
70 local_flush_tlb_kernel_page(vaddr);
71
72 return (void *)vaddr;
73}
74EXPORT_SYMBOL(kmap_atomic);
75
76void kunmap_atomic(void *kvaddr, enum km_type type)
77{
78 unsigned long vaddr = (unsigned long) kvaddr & PAGE_MASK;
79 unsigned int idx = type + KM_TYPE_NR * smp_processor_id();
80
81 if (kvaddr >= (void *)FIXADDR_START) {
82 __cpuc_flush_dcache_page((void *)vaddr);
83#ifdef CONFIG_DEBUG_HIGHMEM
84 BUG_ON(vaddr != __fix_to_virt(FIX_KMAP_BEGIN + idx));
85 set_pte_ext(TOP_PTE(vaddr), __pte(0), 0);
86 local_flush_tlb_kernel_page(vaddr);
87#else
88 (void) idx; /* to kill a warning */
89#endif
Nicolas Pitre7929eb92009-09-03 21:45:59 +010090 } else if (vaddr >= PKMAP_ADDR(0) && vaddr < PKMAP_ADDR(LAST_PKMAP)) {
91 /* this address was obtained through kmap_high_get() */
92 kunmap_high(pte_page(pkmap_page_table[PKMAP_NR(vaddr)]));
Nicolas Pitred73cd422008-09-15 16:44:55 -040093 }
94 pagefault_enable();
95}
96EXPORT_SYMBOL(kunmap_atomic);
97
98void *kmap_atomic_pfn(unsigned long pfn, enum km_type type)
99{
100 unsigned int idx;
101 unsigned long vaddr;
102
103 pagefault_disable();
104
105 idx = type + KM_TYPE_NR * smp_processor_id();
106 vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx);
107#ifdef CONFIG_DEBUG_HIGHMEM
108 BUG_ON(!pte_none(*(TOP_PTE(vaddr))));
109#endif
110 set_pte_ext(TOP_PTE(vaddr), pfn_pte(pfn, kmap_prot), 0);
111 local_flush_tlb_kernel_page(vaddr);
112
113 return (void *)vaddr;
114}
115
116struct page *kmap_atomic_to_page(const void *ptr)
117{
118 unsigned long vaddr = (unsigned long)ptr;
119 pte_t *pte;
120
121 if (vaddr < FIXADDR_START)
122 return virt_to_page(ptr);
123
124 pte = TOP_PTE(vaddr);
125 return pte_page(*pte);
126}