summaryrefslogtreecommitdiffstats
path: root/arch/mn10300/include/asm/highmem.h
blob: 1ddea5afba09344ba8e807e6ce8f5edc5c26241f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
/* MN10300 Virtual kernel memory mappings for high memory
 *
 * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
 * Written by David Howells (dhowells@redhat.com)
 * - Derived from include/asm-i386/highmem.h
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public Licence
 * as published by the Free Software Foundation; either version
 * 2 of the Licence, or (at your option) any later version.
 */
#ifndef _ASM_HIGHMEM_H
#define _ASM_HIGHMEM_H

#ifdef __KERNEL__

#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/highmem.h>
#include <asm/kmap_types.h>
#include <asm/pgtable.h>

/* undef for production */
#undef HIGHMEM_DEBUG

/* declarations for highmem.c */
extern unsigned long highstart_pfn, highend_pfn;

extern pte_t *kmap_pte;
extern pgprot_t kmap_prot;
extern pte_t *pkmap_page_table;

extern void __init kmap_init(void);

/*
 * Right now we initialize only a single pte table. It can be extended
 * easily, subsequent pte tables have to be allocated in one physical
 * chunk of RAM.
 */
#define PKMAP_BASE	0xfe000000UL
#define LAST_PKMAP	1024
#define LAST_PKMAP_MASK (LAST_PKMAP - 1)
#define PKMAP_NR(virt)  ((virt - PKMAP_BASE) >> PAGE_SHIFT)
#define PKMAP_ADDR(nr)  (PKMAP_BASE + ((nr) << PAGE_SHIFT))

extern unsigned long kmap_high(struct page *page);
extern void kunmap_high(struct page *page);

static inline unsigned long kmap(struct page *page)
{
	if (in_interrupt())
		BUG();
	if (page < highmem_start_page)
		return page_address(page);
	return kmap_high(page);
}

static inline void kunmap(struct page *page)
{
	if (in_interrupt())
		BUG();
	if (page < highmem_start_page)
		return;
	kunmap_high(page);
}

/*
 * The use of kmap_atomic/kunmap_atomic is discouraged - kmap/kunmap
 * gives a more generic (and caching) interface. But kmap_atomic can
 * be used in IRQ contexts, so in some (very limited) cases we need
 * it.
 */
static inline void *kmap_atomic(struct page *page)
{
	unsigned long vaddr;
	int idx, type;

	preempt_disable();
	pagefault_disable();
	if (page < highmem_start_page)
		return page_address(page);

	type = kmap_atomic_idx_push();
	idx = type + KM_TYPE_NR * smp_processor_id();
	vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx);
#if HIGHMEM_DEBUG
	if (!pte_none(*(kmap_pte - idx)))
		BUG();
#endif
	set_pte(kmap_pte - idx, mk_pte(page, kmap_prot));
	local_flush_tlb_one(vaddr);

	return (void *)vaddr;
}

static inline void __kunmap_atomic(unsigned long vaddr)
{
	int type;

	if (vaddr < FIXADDR_START) { /* FIXME */
		pagefault_enable();
		preempt_enable();
		return;
	}

	type = kmap_atomic_idx();

#if HIGHMEM_DEBUG
	{
		unsigned int idx;
		idx = type + KM_TYPE_NR * smp_processor_id();

		if (vaddr != __fix_to_virt(FIX_KMAP_BEGIN + idx))
			BUG();

		/*
		 * force other mappings to Oops if they'll try to access
		 * this pte without first remap it
		 */
		pte_clear(kmap_pte - idx);
		local_flush_tlb_one(vaddr);
	}
#endif

	kmap_atomic_idx_pop();
	pagefault_enable();
	preempt_enable();
}
#endif /* __KERNEL__ */

#endif /* _ASM_HIGHMEM_H */