summaryrefslogtreecommitdiffstats
path: root/arch/sparc/mm/iommu.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1998-08-25 09:12:35 +0000
committerRalf Baechle <ralf@linux-mips.org>1998-08-25 09:12:35 +0000
commitc7fc24dc4420057f103afe8fc64524ebc25c5d37 (patch)
tree3682407a599b8f9f03fc096298134cafba1c9b2f /arch/sparc/mm/iommu.c
parent1d793fade8b063fde3cf275bf1a5c2d381292cd9 (diff)
o Merge with Linux 2.1.116.
o New Newport console code. o New G364 console code.
Diffstat (limited to 'arch/sparc/mm/iommu.c')
-rw-r--r--arch/sparc/mm/iommu.c85
1 files changed, 67 insertions, 18 deletions
diff --git a/arch/sparc/mm/iommu.c b/arch/sparc/mm/iommu.c
index e46216233..c7c6bdd5f 100644
--- a/arch/sparc/mm/iommu.c
+++ b/arch/sparc/mm/iommu.c
@@ -1,4 +1,4 @@
-/* $Id: iommu.c,v 1.7 1998/02/22 10:32:26 ecd Exp $
+/* $Id: iommu.c,v 1.9 1998/04/15 14:58:37 jj Exp $
* iommu.c: IOMMU specific routines for memory management.
*
* Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -10,6 +10,7 @@
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/init.h>
+#include <linux/mm.h>
#include <linux/malloc.h>
#include <asm/pgtable.h>
#include <asm/sbus.h>
@@ -26,14 +27,12 @@ static int viking_flush = 0;
extern void viking_flush_page(unsigned long page);
extern void viking_mxcc_flush_page(unsigned long page);
-#define LONG_ALIGN(x) (((x)+(sizeof(long))-1)&~((sizeof(long))-1))
-
#define IOPERM (IOPTE_CACHE | IOPTE_WRITE | IOPTE_VALID)
#define MKIOPTE(phys) (((((phys)>>4) & IOPTE_PAGE) | IOPERM) & ~IOPTE_WAZ)
-static inline void iommu_map_dvma_pages_for_iommu(struct iommu_struct *iommu,
- unsigned long kern_end)
+static inline void iommu_map_dvma_pages_for_iommu(struct iommu_struct *iommu)
{
+ unsigned long kern_end = (unsigned long) high_memory;
unsigned long first = page_offset;
unsigned long last = kern_end;
iopte_t *iopte = iommu->page_table;
@@ -45,18 +44,17 @@ static inline void iommu_map_dvma_pages_for_iommu(struct iommu_struct *iommu,
}
}
-__initfunc(unsigned long
-iommu_init(int iommund, unsigned long memory_start,
- unsigned long memory_end, struct linux_sbus *sbus))
+__initfunc(void
+iommu_init(int iommund, struct linux_sbus *sbus))
{
unsigned int impl, vers, ptsize;
unsigned long tmp;
struct iommu_struct *iommu;
struct linux_prom_registers iommu_promregs[PROMREG_MAX];
+ int i, j, k, l, m;
+ struct iommu_alloc { unsigned long addr; int next; } *ia;
- memory_start = LONG_ALIGN(memory_start);
- iommu = (struct iommu_struct *) memory_start;
- memory_start += sizeof(struct iommu_struct);
+ iommu = kmalloc(sizeof(struct iommu_struct), GFP_ATOMIC);
prom_getproperty(iommund, "reg", (void *) iommu_promregs,
sizeof(iommu_promregs));
iommu->regs = (struct iommu_regs *)
@@ -98,16 +96,68 @@ iommu_init(int iommund, unsigned long memory_start,
ptsize = iommu->end - iommu->start + 1;
ptsize = (ptsize >> PAGE_SHIFT) * sizeof(iopte_t);
- /* Stupid alignment constraints give me a headache. */
- memory_start = PAGE_ALIGN(memory_start);
- memory_start = (((memory_start) + (ptsize - 1)) & ~(ptsize - 1));
- iommu->lowest = iommu->page_table = (iopte_t *) memory_start;
- memory_start += ptsize;
+ /* Stupid alignment constraints give me a headache.
+ We want to get very large aligned memory area, larger than
+ maximum what get_free_pages gives us (128K): we need
+ 256K or 512K or 1M or 2M aligned to its size. */
+ ia = (struct iommu_alloc *) kmalloc (sizeof(struct iommu_alloc) * 128, GFP_ATOMIC);
+ for (i = 0; i < 128; i++) {
+ ia[i].addr = 0;
+ ia[i].next = -1;
+ }
+ k = 0;
+ for (i = 0; i < 128; i++) {
+ ia[i].addr = __get_free_pages(GFP_DMA, 5);
+ if (ia[i].addr <= ia[k].addr) {
+ if (i) {
+ ia[i].next = k;
+ k = i;
+ }
+ } else {
+ for (m = k, l = ia[k].next; l != -1; m = l, l = ia[l].next)
+ if (ia[i].addr <= ia[l].addr) {
+ ia[i].next = l;
+ ia[m].next = i;
+ }
+ if (l == -1)
+ ia[m].next = i;
+ }
+ for (m = -1, j = 0, l = k; l != -1; l = ia[l].next) {
+ if (!(ia[l].addr & (ptsize - 1))) {
+ tmp = ia[l].addr;
+ m = l;
+ j = 128 * 1024;
+ } else if (m != -1) {
+ if (ia[l].addr != tmp + j)
+ m = -1;
+ else {
+ j += 128 * 1024;
+ if (j == ptsize) {
+ break;
+ }
+ }
+ }
+ }
+ if (l != -1)
+ break;
+ }
+ if (i == 128) {
+ prom_printf("Could not allocate iopte of size 0x%08x\n", ptsize);
+ prom_halt();
+ }
+ for (l = m, j = 0; j < ptsize; j += 128 * 1024, l = ia[l].next)
+ ia[l].addr = 0;
+ for (l = k; l != -1; l = ia[l].next)
+ if (ia[l].addr)
+ free_pages(ia[l].addr, 5);
+ kfree (ia);
+ iommu->lowest = iommu->page_table = (iopte_t *)tmp;
+
/* Initialize new table. */
flush_cache_all();
memset(iommu->page_table, 0, ptsize);
- iommu_map_dvma_pages_for_iommu(iommu, memory_end);
+ iommu_map_dvma_pages_for_iommu(iommu);
if(viking_mxcc_present) {
unsigned long start = (unsigned long) iommu->page_table;
unsigned long end = (start + ptsize);
@@ -130,7 +180,6 @@ iommu_init(int iommund, unsigned long memory_start,
sbus->iommu = iommu;
printk("IOMMU: impl %d vers %d page table at %p of size %d bytes\n",
impl, vers, iommu->page_table, ptsize);
- return memory_start;
}
static __u32 iommu_get_scsi_one_noflush(char *vaddr, unsigned long len, struct linux_sbus *sbus)