From c7fc24dc4420057f103afe8fc64524ebc25c5d37 Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Tue, 25 Aug 1998 09:12:35 +0000 Subject: o Merge with Linux 2.1.116. o New Newport console code. o New G364 console code. --- arch/sparc/mm/iommu.c | 85 ++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 67 insertions(+), 18 deletions(-) (limited to 'arch/sparc/mm/iommu.c') 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 #include #include +#include #include #include #include @@ -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) -- cgit v1.2.3