18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Virtual DMA allocation
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * (C) 1999 Thomas Bogendoerfer (tsbogend@alpha.franken.de)
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * 11/26/2000 -- disabled the existing code because it didn't work for
88c2ecf20Sopenharmony_ci * me in 2.4.  Replaced with a significantly more primitive version
98c2ecf20Sopenharmony_ci * similar to the sun3 code.  the old functionality was probably more
108c2ecf20Sopenharmony_ci * desirable, but....   -- Sam Creasey (sammy@oh.verio.com)
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci */
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include <linux/kernel.h>
158c2ecf20Sopenharmony_ci#include <linux/init.h>
168c2ecf20Sopenharmony_ci#include <linux/bitops.h>
178c2ecf20Sopenharmony_ci#include <linux/mm.h>
188c2ecf20Sopenharmony_ci#include <linux/memblock.h>
198c2ecf20Sopenharmony_ci#include <linux/vmalloc.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#include <asm/sun3x.h>
228c2ecf20Sopenharmony_ci#include <asm/dvma.h>
238c2ecf20Sopenharmony_ci#include <asm/io.h>
248c2ecf20Sopenharmony_ci#include <asm/page.h>
258c2ecf20Sopenharmony_ci#include <asm/tlbflush.h>
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci/* IOMMU support */
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci#define IOMMU_ADDR_MASK            0x03ffe000
308c2ecf20Sopenharmony_ci#define IOMMU_CACHE_INHIBIT        0x00000040
318c2ecf20Sopenharmony_ci#define IOMMU_FULL_BLOCK           0x00000020
328c2ecf20Sopenharmony_ci#define IOMMU_MODIFIED             0x00000010
338c2ecf20Sopenharmony_ci#define IOMMU_USED                 0x00000008
348c2ecf20Sopenharmony_ci#define IOMMU_WRITE_PROTECT        0x00000004
358c2ecf20Sopenharmony_ci#define IOMMU_DT_MASK              0x00000003
368c2ecf20Sopenharmony_ci#define IOMMU_DT_INVALID           0x00000000
378c2ecf20Sopenharmony_ci#define IOMMU_DT_VALID             0x00000001
388c2ecf20Sopenharmony_ci#define IOMMU_DT_BAD               0x00000002
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistatic volatile unsigned long *iommu_pte = (unsigned long *)SUN3X_IOMMU;
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci#define dvma_entry_paddr(index)		(iommu_pte[index] & IOMMU_ADDR_MASK)
458c2ecf20Sopenharmony_ci#define dvma_entry_vaddr(index,paddr)	((index << DVMA_PAGE_SHIFT) |  \
468c2ecf20Sopenharmony_ci					 (paddr & (DVMA_PAGE_SIZE-1)))
478c2ecf20Sopenharmony_ci#if 0
488c2ecf20Sopenharmony_ci#define dvma_entry_set(index,addr)	(iommu_pte[index] =            \
498c2ecf20Sopenharmony_ci					    (addr & IOMMU_ADDR_MASK) | \
508c2ecf20Sopenharmony_ci				             IOMMU_DT_VALID | IOMMU_CACHE_INHIBIT)
518c2ecf20Sopenharmony_ci#else
528c2ecf20Sopenharmony_ci#define dvma_entry_set(index,addr)	(iommu_pte[index] =            \
538c2ecf20Sopenharmony_ci					    (addr & IOMMU_ADDR_MASK) | \
548c2ecf20Sopenharmony_ci				             IOMMU_DT_VALID)
558c2ecf20Sopenharmony_ci#endif
568c2ecf20Sopenharmony_ci#define dvma_entry_clr(index)		(iommu_pte[index] = IOMMU_DT_INVALID)
578c2ecf20Sopenharmony_ci#define dvma_entry_hash(addr)		((addr >> DVMA_PAGE_SHIFT) ^ \
588c2ecf20Sopenharmony_ci					 ((addr & 0x03c00000) >>     \
598c2ecf20Sopenharmony_ci						(DVMA_PAGE_SHIFT+4)))
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci#ifdef DEBUG
628c2ecf20Sopenharmony_ci/* code to print out a dvma mapping for debugging purposes */
638c2ecf20Sopenharmony_civoid dvma_print (unsigned long dvma_addr)
648c2ecf20Sopenharmony_ci{
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	unsigned long index;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	index = dvma_addr >> DVMA_PAGE_SHIFT;
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	pr_info("idx %lx dvma_addr %08lx paddr %08lx\n", index, dvma_addr,
718c2ecf20Sopenharmony_ci		dvma_entry_paddr(index));
728c2ecf20Sopenharmony_ci}
738c2ecf20Sopenharmony_ci#endif
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci/* create a virtual mapping for a page assigned within the IOMMU
778c2ecf20Sopenharmony_ci   so that the cpu can reach it easily */
788c2ecf20Sopenharmony_ciinline int dvma_map_cpu(unsigned long kaddr,
798c2ecf20Sopenharmony_ci			       unsigned long vaddr, int len)
808c2ecf20Sopenharmony_ci{
818c2ecf20Sopenharmony_ci	pgd_t *pgd;
828c2ecf20Sopenharmony_ci	p4d_t *p4d;
838c2ecf20Sopenharmony_ci	pud_t *pud;
848c2ecf20Sopenharmony_ci	unsigned long end;
858c2ecf20Sopenharmony_ci	int ret = 0;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	kaddr &= PAGE_MASK;
888c2ecf20Sopenharmony_ci	vaddr &= PAGE_MASK;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	end = PAGE_ALIGN(vaddr + len);
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	pr_debug("dvma: mapping kern %08lx to virt %08lx\n", kaddr, vaddr);
938c2ecf20Sopenharmony_ci	pgd = pgd_offset_k(vaddr);
948c2ecf20Sopenharmony_ci	p4d = p4d_offset(pgd, vaddr);
958c2ecf20Sopenharmony_ci	pud = pud_offset(p4d, vaddr);
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	do {
988c2ecf20Sopenharmony_ci		pmd_t *pmd;
998c2ecf20Sopenharmony_ci		unsigned long end2;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci		if((pmd = pmd_alloc(&init_mm, pud, vaddr)) == NULL) {
1028c2ecf20Sopenharmony_ci			ret = -ENOMEM;
1038c2ecf20Sopenharmony_ci			goto out;
1048c2ecf20Sopenharmony_ci		}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci		if((end & PGDIR_MASK) > (vaddr & PGDIR_MASK))
1078c2ecf20Sopenharmony_ci			end2 = (vaddr + (PGDIR_SIZE-1)) & PGDIR_MASK;
1088c2ecf20Sopenharmony_ci		else
1098c2ecf20Sopenharmony_ci			end2 = end;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci		do {
1128c2ecf20Sopenharmony_ci			pte_t *pte;
1138c2ecf20Sopenharmony_ci			unsigned long end3;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci			if((pte = pte_alloc_kernel(pmd, vaddr)) == NULL) {
1168c2ecf20Sopenharmony_ci				ret = -ENOMEM;
1178c2ecf20Sopenharmony_ci				goto out;
1188c2ecf20Sopenharmony_ci			}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci			if((end2 & PMD_MASK) > (vaddr & PMD_MASK))
1218c2ecf20Sopenharmony_ci				end3 = (vaddr + (PMD_SIZE-1)) & PMD_MASK;
1228c2ecf20Sopenharmony_ci			else
1238c2ecf20Sopenharmony_ci				end3 = end2;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci			do {
1268c2ecf20Sopenharmony_ci				pr_debug("mapping %08lx phys to %08lx\n",
1278c2ecf20Sopenharmony_ci					 __pa(kaddr), vaddr);
1288c2ecf20Sopenharmony_ci				set_pte(pte, pfn_pte(virt_to_pfn(kaddr),
1298c2ecf20Sopenharmony_ci						     PAGE_KERNEL));
1308c2ecf20Sopenharmony_ci				pte++;
1318c2ecf20Sopenharmony_ci				kaddr += PAGE_SIZE;
1328c2ecf20Sopenharmony_ci				vaddr += PAGE_SIZE;
1338c2ecf20Sopenharmony_ci			} while(vaddr < end3);
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci		} while(vaddr < end2);
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	} while(vaddr < end);
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	flush_tlb_all();
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci out:
1428c2ecf20Sopenharmony_ci	return ret;
1438c2ecf20Sopenharmony_ci}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ciinline int dvma_map_iommu(unsigned long kaddr, unsigned long baddr,
1478c2ecf20Sopenharmony_ci				 int len)
1488c2ecf20Sopenharmony_ci{
1498c2ecf20Sopenharmony_ci	unsigned long end, index;
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	index = baddr >> DVMA_PAGE_SHIFT;
1528c2ecf20Sopenharmony_ci	end = ((baddr+len) >> DVMA_PAGE_SHIFT);
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	if(len & ~DVMA_PAGE_MASK)
1558c2ecf20Sopenharmony_ci		end++;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	for(; index < end ; index++) {
1588c2ecf20Sopenharmony_ci//		if(dvma_entry_use(index))
1598c2ecf20Sopenharmony_ci//			BUG();
1608c2ecf20Sopenharmony_ci//		pr_info("mapping pa %lx to ba %lx\n", __pa(kaddr),
1618c2ecf20Sopenharmony_ci//			index << DVMA_PAGE_SHIFT);
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci		dvma_entry_set(index, __pa(kaddr));
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci		iommu_pte[index] |= IOMMU_FULL_BLOCK;
1668c2ecf20Sopenharmony_ci//		dvma_entry_inc(index);
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci		kaddr += DVMA_PAGE_SIZE;
1698c2ecf20Sopenharmony_ci	}
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci#ifdef DEBUG
1728c2ecf20Sopenharmony_ci	for(index = (baddr >> DVMA_PAGE_SHIFT); index < end; index++)
1738c2ecf20Sopenharmony_ci		dvma_print(index << DVMA_PAGE_SHIFT);
1748c2ecf20Sopenharmony_ci#endif
1758c2ecf20Sopenharmony_ci	return 0;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci}
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_civoid dvma_unmap_iommu(unsigned long baddr, int len)
1808c2ecf20Sopenharmony_ci{
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	int index, end;
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	index = baddr >> DVMA_PAGE_SHIFT;
1868c2ecf20Sopenharmony_ci	end = (DVMA_PAGE_ALIGN(baddr+len) >> DVMA_PAGE_SHIFT);
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	for(; index < end ; index++) {
1898c2ecf20Sopenharmony_ci		pr_debug("freeing bus mapping %08x\n",
1908c2ecf20Sopenharmony_ci			 index << DVMA_PAGE_SHIFT);
1918c2ecf20Sopenharmony_ci#if 0
1928c2ecf20Sopenharmony_ci		if(!dvma_entry_use(index))
1938c2ecf20Sopenharmony_ci			pr_info("dvma_unmap freeing unused entry %04x\n",
1948c2ecf20Sopenharmony_ci				index);
1958c2ecf20Sopenharmony_ci		else
1968c2ecf20Sopenharmony_ci			dvma_entry_dec(index);
1978c2ecf20Sopenharmony_ci#endif
1988c2ecf20Sopenharmony_ci		dvma_entry_clr(index);
1998c2ecf20Sopenharmony_ci	}
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci}
202