patch-2.1.101 linux/arch/sparc/mm/iommu.c

Next file: linux/arch/sparc/mm/srmmu.c
Previous file: linux/arch/sparc/mm/io-unit.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.100/linux/arch/sparc/mm/iommu.c linux/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 @@
 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 @@
 	}
 }
 
-__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 @@
 	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 @@
 	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)

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov