xref: /openbmc/linux/arch/m68k/sun3x/dvma.c (revision b2441318)
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