Commit | Line | Data |
---|---|---|
d73cd428 NP |
1 | /* |
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 | ||
a05e54c1 LH |
21 | static inline void set_fixmap_pte(int idx, pte_t pte) |
22 | { | |
23 | unsigned long vaddr = __fix_to_virt(idx); | |
836a2418 RH |
24 | pte_t *ptep = pte_offset_kernel(pmd_off_k(vaddr), vaddr); |
25 | ||
26 | set_pte_ext(ptep, pte, 0); | |
a05e54c1 LH |
27 | local_flush_tlb_kernel_page(vaddr); |
28 | } | |
29 | ||
30 | static inline pte_t get_fixmap_pte(unsigned long vaddr) | |
31 | { | |
836a2418 RH |
32 | pte_t *ptep = pte_offset_kernel(pmd_off_k(vaddr), vaddr); |
33 | ||
34 | return *ptep; | |
a05e54c1 LH |
35 | } |
36 | ||
d73cd428 NP |
37 | void *kmap(struct page *page) |
38 | { | |
39 | might_sleep(); | |
40 | if (!PageHighMem(page)) | |
41 | return page_address(page); | |
42 | return kmap_high(page); | |
43 | } | |
44 | EXPORT_SYMBOL(kmap); | |
45 | ||
46 | void kunmap(struct page *page) | |
47 | { | |
48 | BUG_ON(in_interrupt()); | |
49 | if (!PageHighMem(page)) | |
50 | return; | |
51 | kunmap_high(page); | |
52 | } | |
53 | EXPORT_SYMBOL(kunmap); | |
54 | ||
a24401bc | 55 | void *kmap_atomic(struct page *page) |
d73cd428 NP |
56 | { |
57 | unsigned int idx; | |
58 | unsigned long vaddr; | |
7929eb9c | 59 | void *kmap; |
3e4d3af5 | 60 | int type; |
d73cd428 | 61 | |
2cb7c9cb | 62 | preempt_disable(); |
d73cd428 NP |
63 | pagefault_disable(); |
64 | if (!PageHighMem(page)) | |
65 | return page_address(page); | |
66 | ||
17ebba1f NP |
67 | #ifdef CONFIG_DEBUG_HIGHMEM |
68 | /* | |
69 | * There is no cache coherency issue when non VIVT, so force the | |
70 | * dedicated kmap usage for better debugging purposes in that case. | |
71 | */ | |
72 | if (!cache_is_vivt()) | |
73 | kmap = NULL; | |
74 | else | |
75 | #endif | |
76 | kmap = kmap_high_get(page); | |
7929eb9c NP |
77 | if (kmap) |
78 | return kmap; | |
79 | ||
3e4d3af5 PZ |
80 | type = kmap_atomic_idx_push(); |
81 | ||
a5f4c561 | 82 | idx = FIX_KMAP_BEGIN + type + KM_TYPE_NR * smp_processor_id(); |
4221e2e6 | 83 | vaddr = __fix_to_virt(idx); |
d73cd428 NP |
84 | #ifdef CONFIG_DEBUG_HIGHMEM |
85 | /* | |
86 | * With debugging enabled, kunmap_atomic forces that entry to 0. | |
87 | * Make sure it was indeed properly unmapped. | |
88 | */ | |
836a2418 | 89 | BUG_ON(!pte_none(get_fixmap_pte(vaddr))); |
d73cd428 | 90 | #endif |
d73cd428 NP |
91 | /* |
92 | * When debugging is off, kunmap_atomic leaves the previous mapping | |
67ece144 RK |
93 | * in place, so the contained TLB flush ensures the TLB is updated |
94 | * with the new mapping. | |
d73cd428 | 95 | */ |
a05e54c1 | 96 | set_fixmap_pte(idx, mk_pte(page, kmap_prot)); |
d73cd428 NP |
97 | |
98 | return (void *)vaddr; | |
99 | } | |
a24401bc | 100 | EXPORT_SYMBOL(kmap_atomic); |
d73cd428 | 101 | |
3e4d3af5 | 102 | void __kunmap_atomic(void *kvaddr) |
d73cd428 NP |
103 | { |
104 | unsigned long vaddr = (unsigned long) kvaddr & PAGE_MASK; | |
3e4d3af5 | 105 | int idx, type; |
d73cd428 NP |
106 | |
107 | if (kvaddr >= (void *)FIXADDR_START) { | |
20273941 | 108 | type = kmap_atomic_idx(); |
a5f4c561 | 109 | idx = FIX_KMAP_BEGIN + type + KM_TYPE_NR * smp_processor_id(); |
3e4d3af5 | 110 | |
7e5a69e8 NP |
111 | if (cache_is_vivt()) |
112 | __cpuc_flush_dcache_area((void *)vaddr, PAGE_SIZE); | |
d73cd428 | 113 | #ifdef CONFIG_DEBUG_HIGHMEM |
4221e2e6 | 114 | BUG_ON(vaddr != __fix_to_virt(idx)); |
a05e54c1 | 115 | set_fixmap_pte(idx, __pte(0)); |
d73cd428 NP |
116 | #else |
117 | (void) idx; /* to kill a warning */ | |
118 | #endif | |
20273941 | 119 | kmap_atomic_idx_pop(); |
7929eb9c NP |
120 | } else if (vaddr >= PKMAP_ADDR(0) && vaddr < PKMAP_ADDR(LAST_PKMAP)) { |
121 | /* this address was obtained through kmap_high_get() */ | |
122 | kunmap_high(pte_page(pkmap_page_table[PKMAP_NR(vaddr)])); | |
d73cd428 NP |
123 | } |
124 | pagefault_enable(); | |
2cb7c9cb | 125 | preempt_enable(); |
d73cd428 | 126 | } |
3e4d3af5 | 127 | EXPORT_SYMBOL(__kunmap_atomic); |
d73cd428 | 128 | |
3e4d3af5 | 129 | void *kmap_atomic_pfn(unsigned long pfn) |
d73cd428 | 130 | { |
d73cd428 | 131 | unsigned long vaddr; |
3e4d3af5 | 132 | int idx, type; |
9ff0bb5b | 133 | struct page *page = pfn_to_page(pfn); |
d73cd428 | 134 | |
2cb7c9cb | 135 | preempt_disable(); |
d73cd428 | 136 | pagefault_disable(); |
9ff0bb5b TP |
137 | if (!PageHighMem(page)) |
138 | return page_address(page); | |
d73cd428 | 139 | |
3e4d3af5 | 140 | type = kmap_atomic_idx_push(); |
a5f4c561 | 141 | idx = FIX_KMAP_BEGIN + type + KM_TYPE_NR * smp_processor_id(); |
4221e2e6 | 142 | vaddr = __fix_to_virt(idx); |
d73cd428 | 143 | #ifdef CONFIG_DEBUG_HIGHMEM |
836a2418 | 144 | BUG_ON(!pte_none(get_fixmap_pte(vaddr))); |
d73cd428 | 145 | #endif |
a05e54c1 | 146 | set_fixmap_pte(idx, pfn_pte(pfn, kmap_prot)); |
d73cd428 NP |
147 | |
148 | return (void *)vaddr; | |
149 | } |