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