Implement new v3_memalloc romvec interface for allocating aligned memory.
[openbios.git] / drivers / iommu.c
blob5eb8a600572605992a05b0a429a23cc3e27528c9
1 /**
2 ** Proll (PROM replacement)
3 ** iommu.c: Functions for DVMA management.
4 ** Copyright 1999 Pete Zaitcev
5 ** This code is licensed under GNU General Public License.
6 **/
7 #include "config.h"
8 #include "libopenbios/bindings.h"
9 #include "drivers/drivers.h"
10 #include "iommu.h"
11 #include "libopenbios/ofmem.h"
13 #ifdef CONFIG_DEBUG_IOMMU
14 #define DPRINTF(fmt, args...) \
15 do { printk(fmt , ##args); } while (0)
16 #else
17 #define DPRINTF(fmt, args...)
18 #endif
21 * IOMMU parameters
23 struct iommu {
24 struct iommu_regs *regs;
25 unsigned int *page_table;
26 unsigned long plow; /* Base bus address */
29 static struct iommu ciommu;
31 static void
32 iommu_invalidate(struct iommu_regs *iregs)
34 iregs->tlbflush = 0;
38 * XXX This is a problematic interface. We alloc _memory_ which is uncached.
39 * So if we ever reuse allocations somebody is going to get uncached pages.
40 * Returned address is always aligned by page.
41 * BTW, we were not going to give away anonymous storage, were we not?
43 void *
44 dvma_alloc(int size, unsigned int *pphys)
46 void *va;
47 unsigned int pa, ba;
48 unsigned int npages;
49 unsigned int mva, mpa;
50 unsigned int i;
51 unsigned int *iopte;
52 struct iommu *t = &ciommu;
53 int ret;
55 npages = (size + (PAGE_SIZE-1)) / PAGE_SIZE;
56 ret = ofmem_posix_memalign(&va, npages * PAGE_SIZE, PAGE_SIZE);
57 if (ret != 0)
58 return NULL;
60 ba = (unsigned int)mem_alloc(&cdvmem, npages * PAGE_SIZE, PAGE_SIZE);
61 if (ba == 0)
62 return NULL;
64 pa = (unsigned int)va2pa((unsigned long)va);
67 * Change page attributes in MMU to uncached.
69 mva = (unsigned int) va;
70 mpa = (unsigned int) pa;
71 ofmem_arch_early_map_pages(mpa, mva, npages * PAGE_SIZE, ofmem_arch_io_translation_mode(mpa));
74 * Map into IOMMU page table.
76 mpa = (unsigned int) pa;
77 iopte = &t->page_table[(ba - t->plow) / PAGE_SIZE];
78 for (i = 0; i < npages; i++) {
79 *iopte++ = MKIOPTE(mpa);
80 mpa += PAGE_SIZE;
83 *pphys = ba;
85 return va;
89 * Initialize IOMMU
90 * This looks like initialization of CPU MMU but
91 * the routine is higher in food chain.
93 static struct iommu_regs *
94 iommu_init(struct iommu *t, uint64_t base)
96 unsigned int *ptab;
97 int ptsize;
98 #ifdef CONFIG_DEBUG_IOMMU
99 unsigned int impl, vers;
100 #endif
101 unsigned int tmp;
102 struct iommu_regs *regs;
103 int ret;
104 unsigned long vasize;
106 regs = (struct iommu_regs *)ofmem_map_io(base, IOMMU_REGS);
107 if (regs == NULL) {
108 DPRINTF("Cannot map IOMMU\n");
109 for (;;) { }
111 t->regs = regs;
112 #ifdef CONFIG_DEBUG_IOMMU
113 impl = (regs->control & IOMMU_CTRL_IMPL) >> 28;
114 vers = (regs->control & IOMMU_CTRL_VERS) >> 24;
115 #endif
117 tmp = regs->control;
118 tmp &= ~(IOMMU_CTRL_RNGE);
120 tmp |= (IOMMU_RNGE_32MB | IOMMU_CTRL_ENAB);
121 t->plow = 0xfe000000; /* End - 32 MB */
122 /* Size of VA region that we manage */
123 vasize = 0x2000000; /* 32 MB */
125 regs->control = tmp;
126 iommu_invalidate(regs);
128 /* Allocate IOMMU page table */
129 /* Tremendous alignment causes great waste... */
130 ptsize = (vasize / PAGE_SIZE) * sizeof(int);
131 ret = ofmem_posix_memalign((void *)&ptab, ptsize, ptsize);
132 if (ret != 0) {
133 DPRINTF("Cannot allocate IOMMU table [0x%x]\n", ptsize);
134 for (;;) { }
136 t->page_table = ptab;
138 /* flush_cache_all(); */
139 /** flush_tlb_all(); **/
140 tmp = (unsigned int)va2pa((unsigned long)ptab);
141 regs->base = tmp >> 4;
142 iommu_invalidate(regs);
144 DPRINTF("IOMMU: impl %d vers %d page table at 0x%p (pa 0x%x) of size %d bytes\n",
145 impl, vers, t->page_table, tmp, ptsize);
147 mem_init(&cdvmem, (char*)t->plow, (char *)0xfffff000);
148 return regs;
151 void
152 ob_init_iommu(uint64_t base)
154 struct iommu_regs *regs;
156 regs = iommu_init(&ciommu, base);
158 push_str("/iommu");
159 fword("find-device");
160 PUSH((unsigned long)regs);
161 fword("encode-int");
162 push_str("address");
163 fword("property");
165 PUSH(base >> 32);
166 fword("encode-int");
167 PUSH(base & 0xffffffff);
168 fword("encode-int");
169 fword("encode+");
170 PUSH(IOMMU_REGS);
171 fword("encode-int");
172 fword("encode+");
173 push_str("reg");
174 fword("property");