include upstream ip1000a driver version 2.09f
[linux-2.4.git] / arch / alpha / mm / remap.c
1 #include <linux/vmalloc.h>
2 #include <asm/pgalloc.h>
3
4 /* called with the page_table_lock held */
5 static inline void 
6 remap_area_pte(pte_t * pte, unsigned long address, unsigned long size, 
7                unsigned long phys_addr, unsigned long flags)
8 {
9         unsigned long end;
10
11         address &= ~PMD_MASK;
12         end = address + size;
13         if (end > PMD_SIZE)
14                 end = PMD_SIZE;
15         if (address >= end)
16                 BUG();
17         do {
18                 if (!pte_none(*pte)) {
19                         printk("remap_area_pte: page already exists\n");
20                         BUG();
21                 }
22                 set_pte(pte, 
23                         mk_pte_phys(phys_addr, 
24                                     __pgprot(_PAGE_VALID | _PAGE_ASM | 
25                                              _PAGE_KRE | _PAGE_KWE | flags)));
26                 address += PAGE_SIZE;
27                 phys_addr += PAGE_SIZE;
28                 pte++;
29         } while (address && (address < end));
30 }
31
32 /* called with the page_table_lock held */
33 static inline int 
34 remap_area_pmd(pmd_t * pmd, unsigned long address, unsigned long size, 
35                unsigned long phys_addr, unsigned long flags)
36 {
37         unsigned long end;
38
39         address &= ~PGDIR_MASK;
40         end = address + size;
41         if (end > PGDIR_SIZE)
42                 end = PGDIR_SIZE;
43         phys_addr -= address;
44         if (address >= end)
45                 BUG();
46         do {
47                 pte_t * pte = pte_alloc(&init_mm, pmd, address);
48                 if (!pte)
49                         return -ENOMEM;
50                 remap_area_pte(pte, address, end - address, 
51                                      address + phys_addr, flags);
52                 address = (address + PMD_SIZE) & PMD_MASK;
53                 pmd++;
54         } while (address && (address < end));
55         return 0;
56 }
57
58 int
59 __alpha_remap_area_pages(unsigned long address, unsigned long phys_addr,
60                          unsigned long size, unsigned long flags)
61 {
62         pgd_t * dir;
63         int error = 0;
64         unsigned long end = address + size;
65
66         phys_addr -= address;
67         dir = pgd_offset(&init_mm, address);
68         flush_cache_all();
69         if (address >= end)
70                 BUG();
71         spin_lock(&init_mm.page_table_lock);
72         do {
73                 pmd_t *pmd;
74                 pmd = pmd_alloc(&init_mm, dir, address);
75                 error = -ENOMEM;
76                 if (!pmd)
77                         break;
78                 if (remap_area_pmd(pmd, address, end - address,
79                                    phys_addr + address, flags))
80                         break;
81                 error = 0;
82                 address = (address + PGDIR_SIZE) & PGDIR_MASK;
83                 dir++;
84         } while (address && (address < end));
85         spin_unlock(&init_mm.page_table_lock);
86         return error;
87 }
88