File: /usr/src/linux/arch/mips/baget/baget.c

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