1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0 21da177e4SLinus Torvalds /* 31da177e4SLinus Torvalds * Virtual DMA allocation 41da177e4SLinus Torvalds * 51da177e4SLinus Torvalds * (C) 1999 Thomas Bogendoerfer (tsbogend@alpha.franken.de) 61da177e4SLinus Torvalds * 71da177e4SLinus Torvalds * 11/26/2000 -- disabled the existing code because it didn't work for 81da177e4SLinus Torvalds * me in 2.4. Replaced with a significantly more primitive version 91da177e4SLinus Torvalds * similar to the sun3 code. the old functionality was probably more 101da177e4SLinus Torvalds * desirable, but.... -- Sam Creasey (sammy@oh.verio.com) 111da177e4SLinus Torvalds * 121da177e4SLinus Torvalds */ 131da177e4SLinus Torvalds 141da177e4SLinus Torvalds #include <linux/kernel.h> 151da177e4SLinus Torvalds #include <linux/init.h> 161da177e4SLinus Torvalds #include <linux/bitops.h> 171da177e4SLinus Torvalds #include <linux/mm.h> 181da177e4SLinus Torvalds #include <linux/bootmem.h> 191da177e4SLinus Torvalds #include <linux/vmalloc.h> 201da177e4SLinus Torvalds 211da177e4SLinus Torvalds #include <asm/sun3x.h> 221da177e4SLinus Torvalds #include <asm/dvma.h> 231da177e4SLinus Torvalds #include <asm/io.h> 241da177e4SLinus Torvalds #include <asm/page.h> 251da177e4SLinus Torvalds #include <asm/pgtable.h> 261da177e4SLinus Torvalds #include <asm/pgalloc.h> 271da177e4SLinus Torvalds 281da177e4SLinus Torvalds /* IOMMU support */ 291da177e4SLinus Torvalds 301da177e4SLinus Torvalds #define IOMMU_ADDR_MASK 0x03ffe000 311da177e4SLinus Torvalds #define IOMMU_CACHE_INHIBIT 0x00000040 321da177e4SLinus Torvalds #define IOMMU_FULL_BLOCK 0x00000020 331da177e4SLinus Torvalds #define IOMMU_MODIFIED 0x00000010 341da177e4SLinus Torvalds #define IOMMU_USED 0x00000008 351da177e4SLinus Torvalds #define IOMMU_WRITE_PROTECT 0x00000004 361da177e4SLinus Torvalds #define IOMMU_DT_MASK 0x00000003 371da177e4SLinus Torvalds #define IOMMU_DT_INVALID 0x00000000 381da177e4SLinus Torvalds #define IOMMU_DT_VALID 0x00000001 391da177e4SLinus Torvalds #define IOMMU_DT_BAD 0x00000002 401da177e4SLinus Torvalds 411da177e4SLinus Torvalds 421da177e4SLinus Torvalds static volatile unsigned long *iommu_pte = (unsigned long *)SUN3X_IOMMU; 431da177e4SLinus Torvalds 441da177e4SLinus Torvalds 451da177e4SLinus Torvalds #define dvma_entry_paddr(index) (iommu_pte[index] & IOMMU_ADDR_MASK) 461da177e4SLinus Torvalds #define dvma_entry_vaddr(index,paddr) ((index << DVMA_PAGE_SHIFT) | \ 471da177e4SLinus Torvalds (paddr & (DVMA_PAGE_SIZE-1))) 481da177e4SLinus Torvalds #if 0 491da177e4SLinus Torvalds #define dvma_entry_set(index,addr) (iommu_pte[index] = \ 501da177e4SLinus Torvalds (addr & IOMMU_ADDR_MASK) | \ 511da177e4SLinus Torvalds IOMMU_DT_VALID | IOMMU_CACHE_INHIBIT) 521da177e4SLinus Torvalds #else 531da177e4SLinus Torvalds #define dvma_entry_set(index,addr) (iommu_pte[index] = \ 541da177e4SLinus Torvalds (addr & IOMMU_ADDR_MASK) | \ 551da177e4SLinus Torvalds IOMMU_DT_VALID) 561da177e4SLinus Torvalds #endif 571da177e4SLinus Torvalds #define dvma_entry_clr(index) (iommu_pte[index] = IOMMU_DT_INVALID) 581da177e4SLinus Torvalds #define dvma_entry_hash(addr) ((addr >> DVMA_PAGE_SHIFT) ^ \ 591da177e4SLinus Torvalds ((addr & 0x03c00000) >> \ 601da177e4SLinus Torvalds (DVMA_PAGE_SHIFT+4))) 611da177e4SLinus Torvalds 621da177e4SLinus Torvalds #ifdef DEBUG 631da177e4SLinus Torvalds /* code to print out a dvma mapping for debugging purposes */ 641da177e4SLinus Torvalds void dvma_print (unsigned long dvma_addr) 651da177e4SLinus Torvalds { 661da177e4SLinus Torvalds 671da177e4SLinus Torvalds unsigned long index; 681da177e4SLinus Torvalds 691da177e4SLinus Torvalds index = dvma_addr >> DVMA_PAGE_SHIFT; 701da177e4SLinus Torvalds 714eee1e72SGeert Uytterhoeven pr_info("idx %lx dvma_addr %08lx paddr %08lx\n", index, dvma_addr, 721da177e4SLinus Torvalds dvma_entry_paddr(index)); 731da177e4SLinus Torvalds } 741da177e4SLinus Torvalds #endif 751da177e4SLinus Torvalds 761da177e4SLinus Torvalds 771da177e4SLinus Torvalds /* create a virtual mapping for a page assigned within the IOMMU 781da177e4SLinus Torvalds so that the cpu can reach it easily */ 791da177e4SLinus Torvalds inline int dvma_map_cpu(unsigned long kaddr, 801da177e4SLinus Torvalds unsigned long vaddr, int len) 811da177e4SLinus Torvalds { 821da177e4SLinus Torvalds pgd_t *pgd; 831da177e4SLinus Torvalds unsigned long end; 841da177e4SLinus Torvalds int ret = 0; 851da177e4SLinus Torvalds 861da177e4SLinus Torvalds kaddr &= PAGE_MASK; 871da177e4SLinus Torvalds vaddr &= PAGE_MASK; 881da177e4SLinus Torvalds 891da177e4SLinus Torvalds end = PAGE_ALIGN(vaddr + len); 901da177e4SLinus Torvalds 914eee1e72SGeert Uytterhoeven pr_debug("dvma: mapping kern %08lx to virt %08lx\n", kaddr, vaddr); 921da177e4SLinus Torvalds pgd = pgd_offset_k(vaddr); 931da177e4SLinus Torvalds 941da177e4SLinus Torvalds do { 951da177e4SLinus Torvalds pmd_t *pmd; 961da177e4SLinus Torvalds unsigned long end2; 971da177e4SLinus Torvalds 981da177e4SLinus Torvalds if((pmd = pmd_alloc(&init_mm, pgd, vaddr)) == NULL) { 991da177e4SLinus Torvalds ret = -ENOMEM; 1001da177e4SLinus Torvalds goto out; 1011da177e4SLinus Torvalds } 1021da177e4SLinus Torvalds 1031da177e4SLinus Torvalds if((end & PGDIR_MASK) > (vaddr & PGDIR_MASK)) 1041da177e4SLinus Torvalds end2 = (vaddr + (PGDIR_SIZE-1)) & PGDIR_MASK; 1051da177e4SLinus Torvalds else 1061da177e4SLinus Torvalds end2 = end; 1071da177e4SLinus Torvalds 1081da177e4SLinus Torvalds do { 1091da177e4SLinus Torvalds pte_t *pte; 1101da177e4SLinus Torvalds unsigned long end3; 1111da177e4SLinus Torvalds 112872fec16SHugh Dickins if((pte = pte_alloc_kernel(pmd, vaddr)) == NULL) { 1131da177e4SLinus Torvalds ret = -ENOMEM; 1141da177e4SLinus Torvalds goto out; 1151da177e4SLinus Torvalds } 1161da177e4SLinus Torvalds 1171da177e4SLinus Torvalds if((end2 & PMD_MASK) > (vaddr & PMD_MASK)) 1181da177e4SLinus Torvalds end3 = (vaddr + (PMD_SIZE-1)) & PMD_MASK; 1191da177e4SLinus Torvalds else 1201da177e4SLinus Torvalds end3 = end2; 1211da177e4SLinus Torvalds 1221da177e4SLinus Torvalds do { 1234eee1e72SGeert Uytterhoeven pr_debug("mapping %08lx phys to %08lx\n", 1241da177e4SLinus Torvalds __pa(kaddr), vaddr); 1251da177e4SLinus Torvalds set_pte(pte, pfn_pte(virt_to_pfn(kaddr), 1261da177e4SLinus Torvalds PAGE_KERNEL)); 1271da177e4SLinus Torvalds pte++; 1281da177e4SLinus Torvalds kaddr += PAGE_SIZE; 1291da177e4SLinus Torvalds vaddr += PAGE_SIZE; 1301da177e4SLinus Torvalds } while(vaddr < end3); 1311da177e4SLinus Torvalds 1321da177e4SLinus Torvalds } while(vaddr < end2); 1331da177e4SLinus Torvalds 1341da177e4SLinus Torvalds } while(vaddr < end); 1351da177e4SLinus Torvalds 1361da177e4SLinus Torvalds flush_tlb_all(); 1371da177e4SLinus Torvalds 1381da177e4SLinus Torvalds out: 1391da177e4SLinus Torvalds return ret; 1401da177e4SLinus Torvalds } 1411da177e4SLinus Torvalds 1421da177e4SLinus Torvalds 1431da177e4SLinus Torvalds inline int dvma_map_iommu(unsigned long kaddr, unsigned long baddr, 1441da177e4SLinus Torvalds int len) 1451da177e4SLinus Torvalds { 1461da177e4SLinus Torvalds unsigned long end, index; 1471da177e4SLinus Torvalds 1481da177e4SLinus Torvalds index = baddr >> DVMA_PAGE_SHIFT; 1491da177e4SLinus Torvalds end = ((baddr+len) >> DVMA_PAGE_SHIFT); 1501da177e4SLinus Torvalds 1511da177e4SLinus Torvalds if(len & ~DVMA_PAGE_MASK) 1521da177e4SLinus Torvalds end++; 1531da177e4SLinus Torvalds 1541da177e4SLinus Torvalds for(; index < end ; index++) { 1551da177e4SLinus Torvalds // if(dvma_entry_use(index)) 1561da177e4SLinus Torvalds // BUG(); 1574eee1e72SGeert Uytterhoeven // pr_info("mapping pa %lx to ba %lx\n", __pa(kaddr), 1584eee1e72SGeert Uytterhoeven // index << DVMA_PAGE_SHIFT); 1591da177e4SLinus Torvalds 1601da177e4SLinus Torvalds dvma_entry_set(index, __pa(kaddr)); 1611da177e4SLinus Torvalds 1621da177e4SLinus Torvalds iommu_pte[index] |= IOMMU_FULL_BLOCK; 1631da177e4SLinus Torvalds // dvma_entry_inc(index); 1641da177e4SLinus Torvalds 1651da177e4SLinus Torvalds kaddr += DVMA_PAGE_SIZE; 1661da177e4SLinus Torvalds } 1671da177e4SLinus Torvalds 1681da177e4SLinus Torvalds #ifdef DEBUG 1691da177e4SLinus Torvalds for(index = (baddr >> DVMA_PAGE_SHIFT); index < end; index++) 1701da177e4SLinus Torvalds dvma_print(index << DVMA_PAGE_SHIFT); 1711da177e4SLinus Torvalds #endif 1721da177e4SLinus Torvalds return 0; 1731da177e4SLinus Torvalds 1741da177e4SLinus Torvalds } 1751da177e4SLinus Torvalds 1761da177e4SLinus Torvalds void dvma_unmap_iommu(unsigned long baddr, int len) 1771da177e4SLinus Torvalds { 1781da177e4SLinus Torvalds 1791da177e4SLinus Torvalds int index, end; 1801da177e4SLinus Torvalds 1811da177e4SLinus Torvalds 1821da177e4SLinus Torvalds index = baddr >> DVMA_PAGE_SHIFT; 1831da177e4SLinus Torvalds end = (DVMA_PAGE_ALIGN(baddr+len) >> DVMA_PAGE_SHIFT); 1841da177e4SLinus Torvalds 1851da177e4SLinus Torvalds for(; index < end ; index++) { 1864eee1e72SGeert Uytterhoeven pr_debug("freeing bus mapping %08x\n", 1874eee1e72SGeert Uytterhoeven index << DVMA_PAGE_SHIFT); 1881da177e4SLinus Torvalds #if 0 1891da177e4SLinus Torvalds if(!dvma_entry_use(index)) 1904eee1e72SGeert Uytterhoeven pr_info("dvma_unmap freeing unused entry %04x\n", 1911da177e4SLinus Torvalds index); 1921da177e4SLinus Torvalds else 1931da177e4SLinus Torvalds dvma_entry_dec(index); 1941da177e4SLinus Torvalds #endif 1951da177e4SLinus Torvalds dvma_entry_clr(index); 1961da177e4SLinus Torvalds } 1971da177e4SLinus Torvalds 1981da177e4SLinus Torvalds } 1991da177e4SLinus Torvalds 200