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>
1857c8a661SMike Rapoport #include <linux/memblock.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>
25ca15ca40SMike Rapoport #include <asm/tlbflush.h>
261da177e4SLinus Torvalds
271da177e4SLinus Torvalds /* IOMMU support */
281da177e4SLinus Torvalds
291da177e4SLinus Torvalds #define IOMMU_ADDR_MASK 0x03ffe000
301da177e4SLinus Torvalds #define IOMMU_CACHE_INHIBIT 0x00000040
311da177e4SLinus Torvalds #define IOMMU_FULL_BLOCK 0x00000020
321da177e4SLinus Torvalds #define IOMMU_MODIFIED 0x00000010
331da177e4SLinus Torvalds #define IOMMU_USED 0x00000008
341da177e4SLinus Torvalds #define IOMMU_WRITE_PROTECT 0x00000004
351da177e4SLinus Torvalds #define IOMMU_DT_MASK 0x00000003
361da177e4SLinus Torvalds #define IOMMU_DT_INVALID 0x00000000
371da177e4SLinus Torvalds #define IOMMU_DT_VALID 0x00000001
381da177e4SLinus Torvalds #define IOMMU_DT_BAD 0x00000002
391da177e4SLinus Torvalds
401da177e4SLinus Torvalds
411da177e4SLinus Torvalds static volatile unsigned long *iommu_pte = (unsigned long *)SUN3X_IOMMU;
421da177e4SLinus Torvalds
431da177e4SLinus Torvalds
441da177e4SLinus Torvalds #define dvma_entry_paddr(index) (iommu_pte[index] & IOMMU_ADDR_MASK)
451da177e4SLinus Torvalds #define dvma_entry_vaddr(index,paddr) ((index << DVMA_PAGE_SHIFT) | \
461da177e4SLinus Torvalds (paddr & (DVMA_PAGE_SIZE-1)))
471da177e4SLinus Torvalds #if 0
481da177e4SLinus Torvalds #define dvma_entry_set(index,addr) (iommu_pte[index] = \
491da177e4SLinus Torvalds (addr & IOMMU_ADDR_MASK) | \
501da177e4SLinus Torvalds IOMMU_DT_VALID | IOMMU_CACHE_INHIBIT)
511da177e4SLinus Torvalds #else
521da177e4SLinus Torvalds #define dvma_entry_set(index,addr) (iommu_pte[index] = \
531da177e4SLinus Torvalds (addr & IOMMU_ADDR_MASK) | \
541da177e4SLinus Torvalds IOMMU_DT_VALID)
551da177e4SLinus Torvalds #endif
561da177e4SLinus Torvalds #define dvma_entry_clr(index) (iommu_pte[index] = IOMMU_DT_INVALID)
571da177e4SLinus Torvalds #define dvma_entry_hash(addr) ((addr >> DVMA_PAGE_SHIFT) ^ \
581da177e4SLinus Torvalds ((addr & 0x03c00000) >> \
591da177e4SLinus Torvalds (DVMA_PAGE_SHIFT+4)))
601da177e4SLinus Torvalds
611da177e4SLinus Torvalds #ifdef DEBUG
621da177e4SLinus Torvalds /* code to print out a dvma mapping for debugging purposes */
dvma_print(unsigned long dvma_addr)631da177e4SLinus Torvalds void dvma_print (unsigned long dvma_addr)
641da177e4SLinus Torvalds {
651da177e4SLinus Torvalds
661da177e4SLinus Torvalds unsigned long index;
671da177e4SLinus Torvalds
681da177e4SLinus Torvalds index = dvma_addr >> DVMA_PAGE_SHIFT;
691da177e4SLinus Torvalds
704eee1e72SGeert Uytterhoeven pr_info("idx %lx dvma_addr %08lx paddr %08lx\n", index, dvma_addr,
711da177e4SLinus Torvalds dvma_entry_paddr(index));
721da177e4SLinus Torvalds }
731da177e4SLinus Torvalds #endif
741da177e4SLinus Torvalds
751da177e4SLinus Torvalds
761da177e4SLinus Torvalds /* create a virtual mapping for a page assigned within the IOMMU
771da177e4SLinus Torvalds so that the cpu can reach it easily */
dvma_map_cpu(unsigned long kaddr,unsigned long vaddr,int len)781da177e4SLinus Torvalds inline int dvma_map_cpu(unsigned long kaddr,
791da177e4SLinus Torvalds unsigned long vaddr, int len)
801da177e4SLinus Torvalds {
811da177e4SLinus Torvalds pgd_t *pgd;
8260e50f34SMike Rapoport p4d_t *p4d;
8360e50f34SMike Rapoport pud_t *pud;
841da177e4SLinus Torvalds unsigned long end;
851da177e4SLinus Torvalds int ret = 0;
861da177e4SLinus Torvalds
871da177e4SLinus Torvalds kaddr &= PAGE_MASK;
881da177e4SLinus Torvalds vaddr &= PAGE_MASK;
891da177e4SLinus Torvalds
901da177e4SLinus Torvalds end = PAGE_ALIGN(vaddr + len);
911da177e4SLinus Torvalds
924eee1e72SGeert Uytterhoeven pr_debug("dvma: mapping kern %08lx to virt %08lx\n", kaddr, vaddr);
931da177e4SLinus Torvalds pgd = pgd_offset_k(vaddr);
9460e50f34SMike Rapoport p4d = p4d_offset(pgd, vaddr);
9560e50f34SMike Rapoport pud = pud_offset(p4d, vaddr);
961da177e4SLinus Torvalds
971da177e4SLinus Torvalds do {
981da177e4SLinus Torvalds pmd_t *pmd;
991da177e4SLinus Torvalds unsigned long end2;
1001da177e4SLinus Torvalds
10160e50f34SMike Rapoport if((pmd = pmd_alloc(&init_mm, pud, vaddr)) == NULL) {
1021da177e4SLinus Torvalds ret = -ENOMEM;
1031da177e4SLinus Torvalds goto out;
1041da177e4SLinus Torvalds }
1051da177e4SLinus Torvalds
1061da177e4SLinus Torvalds if((end & PGDIR_MASK) > (vaddr & PGDIR_MASK))
1071da177e4SLinus Torvalds end2 = (vaddr + (PGDIR_SIZE-1)) & PGDIR_MASK;
1081da177e4SLinus Torvalds else
1091da177e4SLinus Torvalds end2 = end;
1101da177e4SLinus Torvalds
1111da177e4SLinus Torvalds do {
1121da177e4SLinus Torvalds pte_t *pte;
1131da177e4SLinus Torvalds unsigned long end3;
1141da177e4SLinus Torvalds
115872fec16SHugh Dickins if((pte = pte_alloc_kernel(pmd, vaddr)) == NULL) {
1161da177e4SLinus Torvalds ret = -ENOMEM;
1171da177e4SLinus Torvalds goto out;
1181da177e4SLinus Torvalds }
1191da177e4SLinus Torvalds
1201da177e4SLinus Torvalds if((end2 & PMD_MASK) > (vaddr & PMD_MASK))
1211da177e4SLinus Torvalds end3 = (vaddr + (PMD_SIZE-1)) & PMD_MASK;
1221da177e4SLinus Torvalds else
1231da177e4SLinus Torvalds end3 = end2;
1241da177e4SLinus Torvalds
1251da177e4SLinus Torvalds do {
1264eee1e72SGeert Uytterhoeven pr_debug("mapping %08lx phys to %08lx\n",
1271da177e4SLinus Torvalds __pa(kaddr), vaddr);
128*8f246087SLinus Walleij set_pte(pte, pfn_pte(virt_to_pfn((void *)kaddr),
1291da177e4SLinus Torvalds PAGE_KERNEL));
1301da177e4SLinus Torvalds pte++;
1311da177e4SLinus Torvalds kaddr += PAGE_SIZE;
1321da177e4SLinus Torvalds vaddr += PAGE_SIZE;
1331da177e4SLinus Torvalds } while(vaddr < end3);
1341da177e4SLinus Torvalds
1351da177e4SLinus Torvalds } while(vaddr < end2);
1361da177e4SLinus Torvalds
1371da177e4SLinus Torvalds } while(vaddr < end);
1381da177e4SLinus Torvalds
1391da177e4SLinus Torvalds flush_tlb_all();
1401da177e4SLinus Torvalds
1411da177e4SLinus Torvalds out:
1421da177e4SLinus Torvalds return ret;
1431da177e4SLinus Torvalds }
1441da177e4SLinus Torvalds
1451da177e4SLinus Torvalds
dvma_map_iommu(unsigned long kaddr,unsigned long baddr,int len)1461da177e4SLinus Torvalds inline int dvma_map_iommu(unsigned long kaddr, unsigned long baddr,
1471da177e4SLinus Torvalds int len)
1481da177e4SLinus Torvalds {
1491da177e4SLinus Torvalds unsigned long end, index;
1501da177e4SLinus Torvalds
1511da177e4SLinus Torvalds index = baddr >> DVMA_PAGE_SHIFT;
1521da177e4SLinus Torvalds end = ((baddr+len) >> DVMA_PAGE_SHIFT);
1531da177e4SLinus Torvalds
1541da177e4SLinus Torvalds if(len & ~DVMA_PAGE_MASK)
1551da177e4SLinus Torvalds end++;
1561da177e4SLinus Torvalds
1571da177e4SLinus Torvalds for(; index < end ; index++) {
1581da177e4SLinus Torvalds // if(dvma_entry_use(index))
1591da177e4SLinus Torvalds // BUG();
1604eee1e72SGeert Uytterhoeven // pr_info("mapping pa %lx to ba %lx\n", __pa(kaddr),
1614eee1e72SGeert Uytterhoeven // index << DVMA_PAGE_SHIFT);
1621da177e4SLinus Torvalds
1631da177e4SLinus Torvalds dvma_entry_set(index, __pa(kaddr));
1641da177e4SLinus Torvalds
1651da177e4SLinus Torvalds iommu_pte[index] |= IOMMU_FULL_BLOCK;
1661da177e4SLinus Torvalds // dvma_entry_inc(index);
1671da177e4SLinus Torvalds
1681da177e4SLinus Torvalds kaddr += DVMA_PAGE_SIZE;
1691da177e4SLinus Torvalds }
1701da177e4SLinus Torvalds
1711da177e4SLinus Torvalds #ifdef DEBUG
1721da177e4SLinus Torvalds for(index = (baddr >> DVMA_PAGE_SHIFT); index < end; index++)
1731da177e4SLinus Torvalds dvma_print(index << DVMA_PAGE_SHIFT);
1741da177e4SLinus Torvalds #endif
1751da177e4SLinus Torvalds return 0;
1761da177e4SLinus Torvalds
1771da177e4SLinus Torvalds }
1781da177e4SLinus Torvalds
dvma_unmap_iommu(unsigned long baddr,int len)1791da177e4SLinus Torvalds void dvma_unmap_iommu(unsigned long baddr, int len)
1801da177e4SLinus Torvalds {
1811da177e4SLinus Torvalds
1821da177e4SLinus Torvalds int index, end;
1831da177e4SLinus Torvalds
1841da177e4SLinus Torvalds
1851da177e4SLinus Torvalds index = baddr >> DVMA_PAGE_SHIFT;
1861da177e4SLinus Torvalds end = (DVMA_PAGE_ALIGN(baddr+len) >> DVMA_PAGE_SHIFT);
1871da177e4SLinus Torvalds
1881da177e4SLinus Torvalds for(; index < end ; index++) {
1894eee1e72SGeert Uytterhoeven pr_debug("freeing bus mapping %08x\n",
1904eee1e72SGeert Uytterhoeven index << DVMA_PAGE_SHIFT);
1911da177e4SLinus Torvalds #if 0
1921da177e4SLinus Torvalds if(!dvma_entry_use(index))
1934eee1e72SGeert Uytterhoeven pr_info("dvma_unmap freeing unused entry %04x\n",
1941da177e4SLinus Torvalds index);
1951da177e4SLinus Torvalds else
1961da177e4SLinus Torvalds dvma_entry_dec(index);
1971da177e4SLinus Torvalds #endif
1981da177e4SLinus Torvalds dvma_entry_clr(index);
1991da177e4SLinus Torvalds }
2001da177e4SLinus Torvalds
2011da177e4SLinus Torvalds }
202