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