File: /usr/src/linux/arch/sh/mm/ioremap.c
1 /* $Id: ioremap.c,v 1.4 2001/06/30 09:18:39 gniibe Exp $
2 *
3 * arch/sh/mm/ioremap.c
4 *
5 * Re-map IO memory to kernel address space so that we can access it.
6 * This is needed for high PCI addresses that aren't mapped in the
7 * 640k-1MB IO memory area on PC's
8 *
9 * (C) Copyright 1995 1996 Linus Torvalds
10 */
11
12 #include <linux/vmalloc.h>
13 #include <asm/io.h>
14 #include <asm/pgalloc.h>
15
16 static inline void remap_area_pte(pte_t * pte, unsigned long address,
17 unsigned long size, unsigned long phys_addr, unsigned long flags)
18 {
19 unsigned long end;
20 pgprot_t pgprot = __pgprot(_PAGE_PRESENT | _PAGE_RW |
21 _PAGE_DIRTY | _PAGE_ACCESSED |
22 _PAGE_HW_SHARED | _PAGE_FLAGS_HARD | flags);
23
24 address &= ~PMD_MASK;
25 end = address + size;
26 if (end > PMD_SIZE)
27 end = PMD_SIZE;
28 if (address >= end)
29 BUG();
30 do {
31 if (!pte_none(*pte)) {
32 printk("remap_area_pte: page already exists\n");
33 BUG();
34 }
35 set_pte(pte, mk_pte_phys(phys_addr, pgprot));
36 address += PAGE_SIZE;
37 phys_addr += PAGE_SIZE;
38 pte++;
39 } while (address && (address < end));
40 }
41
42 static inline int remap_area_pmd(pmd_t * pmd, unsigned long address,
43 unsigned long size, unsigned long phys_addr, unsigned long flags)
44 {
45 unsigned long end;
46
47 address &= ~PGDIR_MASK;
48 end = address + size;
49 if (end > PGDIR_SIZE)
50 end = PGDIR_SIZE;
51 phys_addr -= address;
52 if (address >= end)
53 BUG();
54 do {
55 pte_t * pte = pte_alloc(&init_mm, pmd, address);
56 if (!pte)
57 return -ENOMEM;
58 remap_area_pte(pte, address, end - address, address + phys_addr, flags);
59 address = (address + PMD_SIZE) & PMD_MASK;
60 pmd++;
61 } while (address && (address < end));
62 return 0;
63 }
64
65 int remap_area_pages(unsigned long address, unsigned long phys_addr,
66 unsigned long size, unsigned long flags)
67 {
68 int error;
69 pgd_t * dir;
70 unsigned long end = address + size;
71
72 phys_addr -= address;
73 dir = pgd_offset_k(address);
74 flush_cache_all();
75 if (address >= end)
76 BUG();
77 spin_lock(&init_mm.page_table_lock);
78 do {
79 pmd_t *pmd;
80 pmd = pmd_alloc(&init_mm, dir, address);
81 error = -ENOMEM;
82 if (!pmd)
83 break;
84 if (remap_area_pmd(pmd, address, end - address,
85 phys_addr + address, flags))
86 break;
87 error = 0;
88 address = (address + PGDIR_SIZE) & PGDIR_MASK;
89 dir++;
90 } while (address && (address < end));
91 spin_unlock(&init_mm.page_table_lock);
92 flush_tlb_all();
93 return error;
94 }
95
96 /*
97 * Generic mapping function (not visible outside):
98 */
99
100 /*
101 * Remap an arbitrary physical address space into the kernel virtual
102 * address space. Needed when the kernel wants to access high addresses
103 * directly.
104 *
105 * NOTE! We need to allow non-page-aligned mappings too: we will obviously
106 * have to convert them into an offset in a page-aligned mapping, but the
107 * caller shouldn't need to know that small detail.
108 */
109 void * p3_ioremap(unsigned long phys_addr, unsigned long size, unsigned long flags)
110 {
111 void * addr;
112 struct vm_struct * area;
113 unsigned long offset, last_addr;
114
115 /* Don't allow wraparound or zero size */
116 last_addr = phys_addr + size - 1;
117 if (!size || last_addr < phys_addr)
118 return NULL;
119
120 /*
121 * Don't remap the low PCI/ISA area, it's always mapped..
122 */
123 if (phys_addr >= 0xA0000 && last_addr < 0x100000)
124 return phys_to_virt(phys_addr);
125
126 /*
127 * Don't allow anybody to remap normal RAM that we're using..
128 */
129 if (phys_addr < virt_to_phys(high_memory))
130 return NULL;
131
132 /*
133 * Mappings have to be page-aligned
134 */
135 offset = phys_addr & ~PAGE_MASK;
136 phys_addr &= PAGE_MASK;
137 size = PAGE_ALIGN(last_addr) - phys_addr;
138
139 /*
140 * Ok, go for it..
141 */
142 area = get_vm_area(size, VM_IOREMAP);
143 if (!area)
144 return NULL;
145 addr = area->addr;
146 if (remap_area_pages(VMALLOC_VMADDR(addr), phys_addr, size, flags)) {
147 vfree(addr);
148 return NULL;
149 }
150 return (void *) (offset + (char *)addr);
151 }
152
153 void p3_iounmap(void *addr)
154 {
155 if (addr > high_memory)
156 return vfree((void *) (PAGE_MASK & (unsigned long) addr));
157 }
158