]> git.karo-electronics.de Git - mv-sheeva.git/blob - arch/sh/mm/ioremap_64.c
sh: Integrate the SH-5 onchip_remap() more coherently.
[mv-sheeva.git] / arch / sh / mm / ioremap_64.c
1 /*
2  * arch/sh/mm/ioremap_64.c
3  *
4  * Copyright (C) 2000, 2001  Paolo Alberelli
5  * Copyright (C) 2003 - 2007  Paul Mundt
6  *
7  * Mostly derived from arch/sh/mm/ioremap.c which, in turn is mostly
8  * derived from arch/i386/mm/ioremap.c .
9  *
10  *   (C) Copyright 1995 1996 Linus Torvalds
11  *
12  * This file is subject to the terms and conditions of the GNU General Public
13  * License.  See the file "COPYING" in the main directory of this archive
14  * for more details.
15  */
16 #include <linux/vmalloc.h>
17 #include <linux/ioport.h>
18 #include <linux/module.h>
19 #include <linux/mm.h>
20 #include <linux/io.h>
21 #include <linux/bootmem.h>
22 #include <linux/proc_fs.h>
23 #include <asm/page.h>
24 #include <asm/pgalloc.h>
25 #include <asm/addrspace.h>
26 #include <asm/cacheflush.h>
27 #include <asm/tlbflush.h>
28 #include <asm/mmu.h>
29
30 static struct resource shmedia_iomap = {
31         .name   = "shmedia_iomap",
32         .start  = IOBASE_VADDR + PAGE_SIZE,
33         .end    = IOBASE_END - 1,
34 };
35
36 static void shmedia_mapioaddr(unsigned long pa, unsigned long va,
37                               unsigned long flags);
38 static void shmedia_unmapioaddr(unsigned long vaddr);
39 static void __iomem *shmedia_ioremap(struct resource *res, u32 pa,
40                                      int sz, unsigned long flags);
41
42 /*
43  * We have the same problem as the SPARC, so lets have the same comment:
44  * Our mini-allocator...
45  * Boy this is gross! We need it because we must map I/O for
46  * timers and interrupt controller before the kmalloc is available.
47  */
48
49 #define XNMLN  15
50 #define XNRES  10
51
52 struct xresource {
53         struct resource xres;   /* Must be first */
54         int xflag;              /* 1 == used */
55         char xname[XNMLN+1];
56 };
57
58 static struct xresource xresv[XNRES];
59
60 static struct xresource *xres_alloc(void)
61 {
62         struct xresource *xrp;
63         int n;
64
65         xrp = xresv;
66         for (n = 0; n < XNRES; n++) {
67                 if (xrp->xflag == 0) {
68                         xrp->xflag = 1;
69                         return xrp;
70                 }
71                 xrp++;
72         }
73         return NULL;
74 }
75
76 static void xres_free(struct xresource *xrp)
77 {
78         xrp->xflag = 0;
79 }
80
81 static struct resource *shmedia_find_resource(struct resource *root,
82                                               unsigned long vaddr)
83 {
84         struct resource *res;
85
86         for (res = root->child; res; res = res->sibling)
87                 if (res->start <= vaddr && res->end >= vaddr)
88                         return res;
89
90         return NULL;
91 }
92
93 static void __iomem *shmedia_alloc_io(unsigned long phys, unsigned long size,
94                                       const char *name, unsigned long flags)
95 {
96         static int printed_full;
97         struct xresource *xres;
98         struct resource *res;
99         char *tack;
100         int tlen;
101
102         if (name == NULL)
103                 name = "???";
104
105         xres = xres_alloc();
106         if (xres != 0) {
107                 tack = xres->xname;
108                 res = &xres->xres;
109         } else {
110                 if (!printed_full) {
111                         printk(KERN_NOTICE "%s: done with statics, "
112                                "switching to kmalloc\n", __func__);
113                         printed_full = 1;
114                 }
115                 tlen = strlen(name);
116                 tack = kmalloc(sizeof(struct resource) + tlen + 1, GFP_KERNEL);
117                 if (!tack)
118                         return NULL;
119                 memset(tack, 0, sizeof(struct resource));
120                 res = (struct resource *) tack;
121                 tack += sizeof(struct resource);
122         }
123
124         strncpy(tack, name, XNMLN);
125         tack[XNMLN] = 0;
126         res->name = tack;
127
128         return shmedia_ioremap(res, phys, size, flags);
129 }
130
131 static void __iomem *shmedia_ioremap(struct resource *res, u32 pa, int sz,
132                                      unsigned long flags)
133 {
134         unsigned long offset = ((unsigned long) pa) & (~PAGE_MASK);
135         unsigned long round_sz = (offset + sz + PAGE_SIZE-1) & PAGE_MASK;
136         unsigned long va;
137         unsigned int psz;
138
139         if (allocate_resource(&shmedia_iomap, res, round_sz,
140                               shmedia_iomap.start, shmedia_iomap.end,
141                               PAGE_SIZE, NULL, NULL) != 0) {
142                 panic("alloc_io_res(%s): cannot occupy\n",
143                       (res->name != NULL) ? res->name : "???");
144         }
145
146         va = res->start;
147         pa &= PAGE_MASK;
148
149         psz = (res->end - res->start + (PAGE_SIZE - 1)) / PAGE_SIZE;
150
151         for (psz = res->end - res->start + 1; psz != 0; psz -= PAGE_SIZE) {
152                 shmedia_mapioaddr(pa, va, flags);
153                 va += PAGE_SIZE;
154                 pa += PAGE_SIZE;
155         }
156
157         return (void __iomem *)(unsigned long)(res->start + offset);
158 }
159
160 static void shmedia_free_io(struct resource *res)
161 {
162         unsigned long len = res->end - res->start + 1;
163
164         BUG_ON((len & (PAGE_SIZE - 1)) != 0);
165
166         while (len) {
167                 len -= PAGE_SIZE;
168                 shmedia_unmapioaddr(res->start + len);
169         }
170
171         release_resource(res);
172 }
173
174 static __init_refok void *sh64_get_page(void)
175 {
176         void *page;
177
178         if (after_bootmem)
179                 page = (void *)get_zeroed_page(GFP_KERNEL);
180         else
181                 page = alloc_bootmem_pages(PAGE_SIZE);
182
183         if (!page || ((unsigned long)page & ~PAGE_MASK))
184                 panic("sh64_get_page: Out of memory already?\n");
185
186         return page;
187 }
188
189 static void shmedia_mapioaddr(unsigned long pa, unsigned long va,
190                               unsigned long flags)
191 {
192         pgd_t *pgdp;
193         pud_t *pudp;
194         pmd_t *pmdp;
195         pte_t *ptep, pte;
196         pgprot_t prot;
197
198         pr_debug("shmedia_mapiopage pa %08lx va %08lx\n",  pa, va);
199
200         if (!flags)
201                 flags = 1; /* 1 = CB0-1 device */
202
203         pgdp = pgd_offset_k(va);
204         if (pgd_none(*pgdp) || !pgd_present(*pgdp)) {
205                 pudp = (pud_t *)sh64_get_page();
206                 set_pgd(pgdp, __pgd((unsigned long)pudp | _KERNPG_TABLE));
207         }
208
209         pudp = pud_offset(pgdp, va);
210         if (pud_none(*pudp) || !pud_present(*pudp)) {
211                 pmdp = (pmd_t *)sh64_get_page();
212                 set_pud(pudp, __pud((unsigned long)pmdp | _KERNPG_TABLE));
213         }
214
215         pmdp = pmd_offset(pudp, va);
216         if (pmd_none(*pmdp) || !pmd_present(*pmdp)) {
217                 ptep = (pte_t *)sh64_get_page();
218                 set_pmd(pmdp, __pmd((unsigned long)ptep + _PAGE_TABLE));
219         }
220
221         prot = __pgprot(_PAGE_PRESENT | _PAGE_READ     | _PAGE_WRITE  |
222                         _PAGE_DIRTY   | _PAGE_ACCESSED | _PAGE_SHARED | flags);
223
224         pte = pfn_pte(pa >> PAGE_SHIFT, prot);
225         ptep = pte_offset_kernel(pmdp, va);
226
227         if (!pte_none(*ptep) &&
228             pte_val(*ptep) != pte_val(pte))
229                 pte_ERROR(*ptep);
230
231         set_pte(ptep, pte);
232
233         flush_tlb_kernel_range(va, PAGE_SIZE);
234 }
235
236 static void shmedia_unmapioaddr(unsigned long vaddr)
237 {
238         pgd_t *pgdp;
239         pud_t *pudp;
240         pmd_t *pmdp;
241         pte_t *ptep;
242
243         pgdp = pgd_offset_k(vaddr);
244         if (pgd_none(*pgdp) || pgd_bad(*pgdp))
245                 return;
246
247         pudp = pud_offset(pgdp, vaddr);
248         if (pud_none(*pudp) || pud_bad(*pudp))
249                 return;
250
251         pmdp = pmd_offset(pudp, vaddr);
252         if (pmd_none(*pmdp) || pmd_bad(*pmdp))
253                 return;
254
255         ptep = pte_offset_kernel(pmdp, vaddr);
256
257         if (pte_none(*ptep) || !pte_present(*ptep))
258                 return;
259
260         clear_page((void *)ptep);
261         pte_clear(&init_mm, vaddr, ptep);
262 }
263
264 void __iomem *__ioremap(unsigned long offset, unsigned long size,
265                         unsigned long flags)
266 {
267         char name[14];
268
269         sprintf(name, "phys_%08x", (u32)offset);
270         return shmedia_alloc_io(offset, size, name, flags);
271 }
272 EXPORT_SYMBOL(__ioremap);
273
274 void __iounmap(void __iomem *virtual)
275 {
276         unsigned long vaddr = (unsigned long)virtual & PAGE_MASK;
277         struct resource *res;
278         unsigned int psz;
279
280         res = shmedia_find_resource(&shmedia_iomap, vaddr);
281         if (!res) {
282                 printk(KERN_ERR "%s: Failed to free 0x%08lx\n",
283                        __func__, vaddr);
284                 return;
285         }
286
287         psz = (res->end - res->start + (PAGE_SIZE - 1)) / PAGE_SIZE;
288
289         shmedia_free_io(res);
290
291         if ((char *)res >= (char *)xresv &&
292             (char *)res <  (char *)&xresv[XNRES]) {
293                 xres_free((struct xresource *)res);
294         } else {
295                 kfree(res);
296         }
297 }
298 EXPORT_SYMBOL(__iounmap);
299
300 static int
301 ioremap_proc_info(char *buf, char **start, off_t fpos, int length, int *eof,
302                   void *data)
303 {
304         char *p = buf, *e = buf + length;
305         struct resource *r;
306         const char *nm;
307
308         for (r = ((struct resource *)data)->child; r != NULL; r = r->sibling) {
309                 if (p + 32 >= e)        /* Better than nothing */
310                         break;
311                 nm = r->name;
312                 if (nm == NULL)
313                         nm = "???";
314
315                 p += sprintf(p, "%08lx-%08lx: %s\n",
316                              (unsigned long)r->start,
317                              (unsigned long)r->end, nm);
318         }
319
320         return p-buf;
321 }
322
323 static int __init register_proc_onchip(void)
324 {
325         create_proc_read_entry("io_map", 0, 0, ioremap_proc_info,
326                                &shmedia_iomap);
327         return 0;
328 }
329 late_initcall(register_proc_onchip);