ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / arch / mips / baget / baget.c
1 /*
2  * baget.c: Baget low level stuff
3  *
4  * Copyright (C) 1998 Gleb Raiko & Vladimir Roganov
5  *
6  */
7 #include <stdarg.h>
8
9 #include <linux/kernel.h>
10 #include <linux/mm.h>
11 #include <asm/system.h>
12 #include <asm/bootinfo.h>
13 #include <asm/mipsregs.h>
14 #include <asm/pgtable.h>
15
16 #include <asm/baget/baget.h>
17
18 /*
19  *  Following code is based on routines from 'mm/vmalloc.c'
20  *  Additional parameters  ioaddr  is needed to iterate across real I/O address.
21  */
22 static inline int alloc_area_pte(pte_t * pte, unsigned long address,
23                                  unsigned long size, unsigned long ioaddr)
24 {
25         unsigned long end;
26
27         address &= ~PMD_MASK;
28         end = address + size;
29         if (end > PMD_SIZE)
30                 end = PMD_SIZE;
31         while (address < end) {
32                 unsigned long page;
33                 if (!pte_none(*pte))
34                         printk("kseg2_alloc_io: page already exists\n");
35                 /*
36                  *  For MIPS looks pretty to have transparent mapping
37                  *  for KSEG2 areas  -- user can't access one, and no
38                  *  problems with  virtual <--> physical  translation.
39                  */
40                 page = ioaddr & PAGE_MASK;
41
42                 set_pte(pte, __pte(page | pgprot_val(PAGE_USERIO) |
43                                   _PAGE_GLOBAL | __READABLE | __WRITEABLE));
44                 address += PAGE_SIZE;
45                 ioaddr  += PAGE_SIZE;
46                 pte++;
47         }
48         return 0;
49 }
50
51 static inline int alloc_area_pmd(pmd_t * pmd, unsigned long address,
52                                  unsigned long size, unsigned long ioaddr)
53 {
54         unsigned long end;
55
56         address &= ~PGDIR_MASK;
57         end = address + size;
58         if (end > PGDIR_SIZE)
59                 end = PGDIR_SIZE;
60         while (address < end) {
61                 pte_t * pte = pte_alloc_kernel(pmd, address);
62                 if (!pte)
63                         return -ENOMEM;
64                 if (alloc_area_pte(pte, address, end - address, ioaddr))
65                         return -ENOMEM;
66                 address = (address + PMD_SIZE) & PMD_MASK;
67                 ioaddr  += PMD_SIZE;
68                 pmd++;
69         }
70         return 0;
71 }
72
73 int kseg2_alloc_io (unsigned long address, unsigned long size)
74 {
75         pgd_t * dir;
76         unsigned long end = address + size;
77
78         dir = pgd_offset_k(address);
79         flush_cache_all();
80         while (address < end) {
81                 pmd_t *pmd;
82                 pgd_t olddir = *dir;
83
84                 pmd = pmd_alloc_kernel(dir, address);
85                 if (!pmd)
86                         return -ENOMEM;
87                 if (alloc_area_pmd(pmd, address, end - address, address))
88                         return -ENOMEM;
89                 if (pgd_val(olddir) != pgd_val(*dir))
90                         set_pgdir(address, *dir);
91                 address = (address + PGDIR_SIZE) & PGDIR_MASK;
92                 dir++;
93         }
94         flush_tlb_all();
95         return 0;
96 }