162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * linux/arch/m68k/mm/sun3kmap.c
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (C) 2002 Sam Creasey <sammy@sammy.net>
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public
762306a36Sopenharmony_ci * License.  See the file COPYING in the main directory of this archive
862306a36Sopenharmony_ci * for more details.
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/module.h>
1262306a36Sopenharmony_ci#include <linux/types.h>
1362306a36Sopenharmony_ci#include <linux/kernel.h>
1462306a36Sopenharmony_ci#include <linux/mm.h>
1562306a36Sopenharmony_ci#include <linux/vmalloc.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include <asm/page.h>
1862306a36Sopenharmony_ci#include <asm/io.h>
1962306a36Sopenharmony_ci#include <asm/sun3mmu.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#undef SUN3_KMAP_DEBUG
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#ifdef SUN3_KMAP_DEBUG
2462306a36Sopenharmony_ciextern void print_pte_vaddr(unsigned long vaddr);
2562306a36Sopenharmony_ci#endif
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ciextern void mmu_emu_map_pmeg (int context, int vaddr);
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistatic inline void do_page_mapin(unsigned long phys, unsigned long virt,
3062306a36Sopenharmony_ci				 unsigned long type)
3162306a36Sopenharmony_ci{
3262306a36Sopenharmony_ci	unsigned long pte;
3362306a36Sopenharmony_ci	pte_t ptep;
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	ptep = pfn_pte(phys >> PAGE_SHIFT, PAGE_KERNEL);
3662306a36Sopenharmony_ci	pte = pte_val(ptep);
3762306a36Sopenharmony_ci	pte |= type;
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	sun3_put_pte(virt, pte);
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci#ifdef SUN3_KMAP_DEBUG
4262306a36Sopenharmony_ci	pr_info("mapin:");
4362306a36Sopenharmony_ci	print_pte_vaddr(virt);
4462306a36Sopenharmony_ci#endif
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci}
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic inline void do_pmeg_mapin(unsigned long phys, unsigned long virt,
4962306a36Sopenharmony_ci				 unsigned long type, int pages)
5062306a36Sopenharmony_ci{
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	if(sun3_get_segmap(virt & ~SUN3_PMEG_MASK) == SUN3_INVALID_PMEG)
5362306a36Sopenharmony_ci		mmu_emu_map_pmeg(sun3_get_context(), virt);
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	while(pages) {
5662306a36Sopenharmony_ci		do_page_mapin(phys, virt, type);
5762306a36Sopenharmony_ci		phys += PAGE_SIZE;
5862306a36Sopenharmony_ci		virt += PAGE_SIZE;
5962306a36Sopenharmony_ci		pages--;
6062306a36Sopenharmony_ci	}
6162306a36Sopenharmony_ci}
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_civoid __iomem *sun3_ioremap(unsigned long phys, unsigned long size,
6462306a36Sopenharmony_ci		   unsigned long type)
6562306a36Sopenharmony_ci{
6662306a36Sopenharmony_ci	struct vm_struct *area;
6762306a36Sopenharmony_ci	unsigned long offset, virt, ret;
6862306a36Sopenharmony_ci	int pages;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	if(!size)
7162306a36Sopenharmony_ci		return NULL;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	/* page align */
7462306a36Sopenharmony_ci	offset = phys & (PAGE_SIZE-1);
7562306a36Sopenharmony_ci	phys &= ~(PAGE_SIZE-1);
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	size += offset;
7862306a36Sopenharmony_ci	size = PAGE_ALIGN(size);
7962306a36Sopenharmony_ci	if((area = get_vm_area(size, VM_IOREMAP)) == NULL)
8062306a36Sopenharmony_ci		return NULL;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci#ifdef SUN3_KMAP_DEBUG
8362306a36Sopenharmony_ci	pr_info("ioremap: got virt %p size %lx(%lx)\n", area->addr, size,
8462306a36Sopenharmony_ci		area->size);
8562306a36Sopenharmony_ci#endif
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	pages = size / PAGE_SIZE;
8862306a36Sopenharmony_ci	virt = (unsigned long)area->addr;
8962306a36Sopenharmony_ci	ret = virt + offset;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	while(pages) {
9262306a36Sopenharmony_ci		int seg_pages;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci		seg_pages = (SUN3_PMEG_SIZE - (virt & SUN3_PMEG_MASK)) / PAGE_SIZE;
9562306a36Sopenharmony_ci		if(seg_pages > pages)
9662306a36Sopenharmony_ci			seg_pages = pages;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci		do_pmeg_mapin(phys, virt, type, seg_pages);
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci		pages -= seg_pages;
10162306a36Sopenharmony_ci		phys += seg_pages * PAGE_SIZE;
10262306a36Sopenharmony_ci		virt += seg_pages * PAGE_SIZE;
10362306a36Sopenharmony_ci	}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	return (void __iomem *)ret;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci}
10862306a36Sopenharmony_ciEXPORT_SYMBOL(sun3_ioremap);
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_civoid __iomem *__ioremap(unsigned long phys, unsigned long size, int cache)
11262306a36Sopenharmony_ci{
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	return sun3_ioremap(phys, size, SUN3_PAGE_TYPE_IO);
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci}
11762306a36Sopenharmony_ciEXPORT_SYMBOL(__ioremap);
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_civoid iounmap(void __iomem *addr)
12062306a36Sopenharmony_ci{
12162306a36Sopenharmony_ci	vfree((void *)(PAGE_MASK & (unsigned long)addr));
12262306a36Sopenharmony_ci}
12362306a36Sopenharmony_ciEXPORT_SYMBOL(iounmap);
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci/* sun3_map_test(addr, val) -- Reads a byte from addr, storing to val,
12662306a36Sopenharmony_ci * trapping the potential read fault.  Returns 0 if the access faulted,
12762306a36Sopenharmony_ci * 1 on success.
12862306a36Sopenharmony_ci *
12962306a36Sopenharmony_ci * This function is primarily used to check addresses on the VME bus.
13062306a36Sopenharmony_ci *
13162306a36Sopenharmony_ci * Mucking with the page fault handler seems a little hackish to me, but
13262306a36Sopenharmony_ci * SunOS, NetBSD, and Mach all implemented this check in such a manner,
13362306a36Sopenharmony_ci * so I figure we're allowed.
13462306a36Sopenharmony_ci */
13562306a36Sopenharmony_ciint sun3_map_test(unsigned long addr, char *val)
13662306a36Sopenharmony_ci{
13762306a36Sopenharmony_ci	int ret = 0;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	__asm__ __volatile__
14062306a36Sopenharmony_ci		(".globl _sun3_map_test_start\n"
14162306a36Sopenharmony_ci		 "_sun3_map_test_start:\n"
14262306a36Sopenharmony_ci		 "1: moveb (%2), (%0)\n"
14362306a36Sopenharmony_ci		 "   moveq #1, %1\n"
14462306a36Sopenharmony_ci		 "2:\n"
14562306a36Sopenharmony_ci		 ".section .fixup,\"ax\"\n"
14662306a36Sopenharmony_ci		 ".even\n"
14762306a36Sopenharmony_ci		 "3: moveq #0, %1\n"
14862306a36Sopenharmony_ci		 "   jmp 2b\n"
14962306a36Sopenharmony_ci		 ".previous\n"
15062306a36Sopenharmony_ci		 ".section __ex_table,\"a\"\n"
15162306a36Sopenharmony_ci		 ".align 4\n"
15262306a36Sopenharmony_ci		 ".long 1b,3b\n"
15362306a36Sopenharmony_ci		 ".previous\n"
15462306a36Sopenharmony_ci		 ".globl _sun3_map_test_end\n"
15562306a36Sopenharmony_ci		 "_sun3_map_test_end:\n"
15662306a36Sopenharmony_ci		 : "=a"(val), "=r"(ret)
15762306a36Sopenharmony_ci		 : "a"(addr));
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	return ret;
16062306a36Sopenharmony_ci}
16162306a36Sopenharmony_ciEXPORT_SYMBOL(sun3_map_test);
162