aboutsummaryrefslogtreecommitdiffstats
path: root/arch/m68k/mm/sun3kmap.c
blob: 8caa45908cb1f69d4e24f117c1d13dbbf3beeebe (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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
/*
 * linux/arch/m68k/mm/sun3kmap.c
 *
 * Copyright (C) 2002 Sam Creasey <sammy@sammy.net>
 *
 * This file is subject to the terms and conditions of the GNU General Public
 * License.  See the file COPYING in the main directory of this archive
 * for more details.
 */

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/vmalloc.h>

#include <asm/page.h>
#include <asm/pgtable.h>
#include <asm/io.h>
#include <asm/sun3mmu.h>

#undef SUN3_KMAP_DEBUG

#ifdef SUN3_KMAP_DEBUG
extern void print_pte_vaddr(unsigned long vaddr);
#endif

extern void mmu_emu_map_pmeg (int context, int vaddr);

static inline void do_page_mapin(unsigned long phys, unsigned long virt,
				 unsigned long type)
{
	unsigned long pte;
	pte_t ptep;

	ptep = pfn_pte(phys >> PAGE_SHIFT, PAGE_KERNEL);
	pte = pte_val(ptep);
	pte |= type;

	sun3_put_pte(virt, pte);

#ifdef SUN3_KMAP_DEBUG
	print_pte_vaddr(virt);
#endif

}

static inline void do_pmeg_mapin(unsigned long phys, unsigned long virt,
				 unsigned long type, int pages)
{

	if(sun3_get_segmap(virt & ~SUN3_PMEG_MASK) == SUN3_INVALID_PMEG)
		mmu_emu_map_pmeg(sun3_get_context(), virt);

	while(pages) {
		do_page_mapin(phys, virt, type);
		phys += PAGE_SIZE;
		virt += PAGE_SIZE;
		pages--;
	}
}

void __iomem *sun3_ioremap(unsigned long phys, unsigned long size,
		   unsigned long type)
{
	struct vm_struct *area;
	unsigned long offset, virt, ret;
	int pages;

	if(!size)
		return NULL;

	/* page align */
	offset = phys & (PAGE_SIZE-1);
	phys &= ~(PAGE_SIZE-1);

	size += offset;
	size = PAGE_ALIGN(size);
	if((area = get_vm_area(size, VM_IOREMAP)) == NULL)
		return NULL;

#ifdef SUN3_KMAP_DEBUG
	printk("ioremap: got virt %p size %lx(%lx)\n",
	       area->addr, size, area->size);
#endif

	pages = size / PAGE_SIZE;
	virt = (unsigned long)area->addr;
	ret = virt + offset;

	while(pages) {
		int seg_pages;

		seg_pages = (SUN3_PMEG_SIZE - (virt & SUN3_PMEG_MASK)) / PAGE_SIZE;
		if(seg_pages > pages)
			seg_pages = pages;

		do_pmeg_mapin(phys, virt, type, seg_pages);

		pages -= seg_pages;
		phys += seg_pages * PAGE_SIZE;
		virt += seg_pages * PAGE_SIZE;
	}

	return (void __iomem *)ret;

}


void __iomem *__ioremap(unsigned long phys, unsigned long size, int cache)
{

	return sun3_ioremap(phys, size, SUN3_PAGE_TYPE_IO);

}

void iounmap(void __iomem *addr)
{
	vfree((void *)(PAGE_MASK & (unsigned long)addr));
}

/* sun3_map_test(addr, val) -- Reads a byte from addr, storing to val,
 * trapping the potential read fault.  Returns 0 if the access faulted,
 * 1 on success.
 *
 * This function is primarily used to check addresses on the VME bus.
 *
 * Mucking with the page fault handler seems a little hackish to me, but
 * SunOS, NetBSD, and Mach all implemented this check in such a manner,
 * so I figure we're allowed.
 */
int sun3_map_test(unsigned long addr, char *val)
{
	int ret = 0;

	__asm__ __volatile__
		(".globl _sun3_map_test_start\n"
		 "_sun3_map_test_start:\n"
		 "1: moveb (%2), (%0)\n"
		 "   moveq #1, %1\n"
		 "2:\n"
		 ".section .fixup,\"ax\"\n"
		 ".even\n"
		 "3: moveq #0, %1\n"
		 "   jmp 2b\n"
		 ".previous\n"
		 ".section __ex_table,\"a\"\n"
		 ".align 4\n"
		 ".long 1b,3b\n"
		 ".previous\n"
		 ".globl _sun3_map_test_end\n"
		 "_sun3_map_test_end:\n"
		 : "=a"(val), "=r"(ret)
		 : "a"(addr));

	return ret;
}