18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * arch/powerpc/sysdev/dart_iommu.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2004 Olof Johansson <olof@lixom.net>, IBM Corporation 68c2ecf20Sopenharmony_ci * Copyright (C) 2005 Benjamin Herrenschmidt <benh@kernel.crashing.org>, 78c2ecf20Sopenharmony_ci * IBM Corporation 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Based on pSeries_iommu.c: 108c2ecf20Sopenharmony_ci * Copyright (C) 2001 Mike Corrigan & Dave Engebretsen, IBM Corporation 118c2ecf20Sopenharmony_ci * Copyright (C) 2004 Olof Johansson <olof@lixom.net>, IBM Corporation 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * Dynamic DMA mapping support, Apple U3, U4 & IBM CPC925 "DART" iommu. 148c2ecf20Sopenharmony_ci */ 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <linux/init.h> 178c2ecf20Sopenharmony_ci#include <linux/types.h> 188c2ecf20Sopenharmony_ci#include <linux/mm.h> 198c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 208c2ecf20Sopenharmony_ci#include <linux/string.h> 218c2ecf20Sopenharmony_ci#include <linux/pci.h> 228c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 238c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 248c2ecf20Sopenharmony_ci#include <linux/suspend.h> 258c2ecf20Sopenharmony_ci#include <linux/memblock.h> 268c2ecf20Sopenharmony_ci#include <linux/gfp.h> 278c2ecf20Sopenharmony_ci#include <linux/kmemleak.h> 288c2ecf20Sopenharmony_ci#include <asm/io.h> 298c2ecf20Sopenharmony_ci#include <asm/prom.h> 308c2ecf20Sopenharmony_ci#include <asm/iommu.h> 318c2ecf20Sopenharmony_ci#include <asm/pci-bridge.h> 328c2ecf20Sopenharmony_ci#include <asm/machdep.h> 338c2ecf20Sopenharmony_ci#include <asm/cacheflush.h> 348c2ecf20Sopenharmony_ci#include <asm/ppc-pci.h> 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#include "dart.h" 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci/* DART table address and size */ 398c2ecf20Sopenharmony_cistatic u32 *dart_tablebase; 408c2ecf20Sopenharmony_cistatic unsigned long dart_tablesize; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci/* Mapped base address for the dart */ 438c2ecf20Sopenharmony_cistatic unsigned int __iomem *dart; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci/* Dummy val that entries are set to when unused */ 468c2ecf20Sopenharmony_cistatic unsigned int dart_emptyval; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic struct iommu_table iommu_table_dart; 498c2ecf20Sopenharmony_cistatic int iommu_table_dart_inited; 508c2ecf20Sopenharmony_cistatic int dart_dirty; 518c2ecf20Sopenharmony_cistatic int dart_is_u4; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci#define DART_U4_BYPASS_BASE 0x8000000000ull 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci#define DBG(...) 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(invalidate_lock); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic inline void dart_tlb_invalidate_all(void) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci unsigned long l = 0; 628c2ecf20Sopenharmony_ci unsigned int reg, inv_bit; 638c2ecf20Sopenharmony_ci unsigned long limit; 648c2ecf20Sopenharmony_ci unsigned long flags; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci spin_lock_irqsave(&invalidate_lock, flags); 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci DBG("dart: flush\n"); 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci /* To invalidate the DART, set the DARTCNTL_FLUSHTLB bit in the 718c2ecf20Sopenharmony_ci * control register and wait for it to clear. 728c2ecf20Sopenharmony_ci * 738c2ecf20Sopenharmony_ci * Gotcha: Sometimes, the DART won't detect that the bit gets 748c2ecf20Sopenharmony_ci * set. If so, clear it and set it again. 758c2ecf20Sopenharmony_ci */ 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci limit = 0; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci inv_bit = dart_is_u4 ? DART_CNTL_U4_FLUSHTLB : DART_CNTL_U3_FLUSHTLB; 808c2ecf20Sopenharmony_ciretry: 818c2ecf20Sopenharmony_ci l = 0; 828c2ecf20Sopenharmony_ci reg = DART_IN(DART_CNTL); 838c2ecf20Sopenharmony_ci reg |= inv_bit; 848c2ecf20Sopenharmony_ci DART_OUT(DART_CNTL, reg); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci while ((DART_IN(DART_CNTL) & inv_bit) && l < (1L << limit)) 878c2ecf20Sopenharmony_ci l++; 888c2ecf20Sopenharmony_ci if (l == (1L << limit)) { 898c2ecf20Sopenharmony_ci if (limit < 4) { 908c2ecf20Sopenharmony_ci limit++; 918c2ecf20Sopenharmony_ci reg = DART_IN(DART_CNTL); 928c2ecf20Sopenharmony_ci reg &= ~inv_bit; 938c2ecf20Sopenharmony_ci DART_OUT(DART_CNTL, reg); 948c2ecf20Sopenharmony_ci goto retry; 958c2ecf20Sopenharmony_ci } else 968c2ecf20Sopenharmony_ci panic("DART: TLB did not flush after waiting a long " 978c2ecf20Sopenharmony_ci "time. Buggy U3 ?"); 988c2ecf20Sopenharmony_ci } 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&invalidate_lock, flags); 1018c2ecf20Sopenharmony_ci} 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_cistatic inline void dart_tlb_invalidate_one(unsigned long bus_rpn) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci unsigned int reg; 1068c2ecf20Sopenharmony_ci unsigned int l, limit; 1078c2ecf20Sopenharmony_ci unsigned long flags; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci spin_lock_irqsave(&invalidate_lock, flags); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci reg = DART_CNTL_U4_ENABLE | DART_CNTL_U4_IONE | 1128c2ecf20Sopenharmony_ci (bus_rpn & DART_CNTL_U4_IONE_MASK); 1138c2ecf20Sopenharmony_ci DART_OUT(DART_CNTL, reg); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci limit = 0; 1168c2ecf20Sopenharmony_ciwait_more: 1178c2ecf20Sopenharmony_ci l = 0; 1188c2ecf20Sopenharmony_ci while ((DART_IN(DART_CNTL) & DART_CNTL_U4_IONE) && l < (1L << limit)) { 1198c2ecf20Sopenharmony_ci rmb(); 1208c2ecf20Sopenharmony_ci l++; 1218c2ecf20Sopenharmony_ci } 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci if (l == (1L << limit)) { 1248c2ecf20Sopenharmony_ci if (limit < 4) { 1258c2ecf20Sopenharmony_ci limit++; 1268c2ecf20Sopenharmony_ci goto wait_more; 1278c2ecf20Sopenharmony_ci } else 1288c2ecf20Sopenharmony_ci panic("DART: TLB did not flush after waiting a long " 1298c2ecf20Sopenharmony_ci "time. Buggy U4 ?"); 1308c2ecf20Sopenharmony_ci } 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&invalidate_lock, flags); 1338c2ecf20Sopenharmony_ci} 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_cistatic void dart_cache_sync(unsigned int *base, unsigned int count) 1368c2ecf20Sopenharmony_ci{ 1378c2ecf20Sopenharmony_ci /* 1388c2ecf20Sopenharmony_ci * We add 1 to the number of entries to flush, following a 1398c2ecf20Sopenharmony_ci * comment in Darwin indicating that the memory controller 1408c2ecf20Sopenharmony_ci * can prefetch unmapped memory under some circumstances. 1418c2ecf20Sopenharmony_ci */ 1428c2ecf20Sopenharmony_ci unsigned long start = (unsigned long)base; 1438c2ecf20Sopenharmony_ci unsigned long end = start + (count + 1) * sizeof(unsigned int); 1448c2ecf20Sopenharmony_ci unsigned int tmp; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci /* Perform a standard cache flush */ 1478c2ecf20Sopenharmony_ci flush_dcache_range(start, end); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci /* 1508c2ecf20Sopenharmony_ci * Perform the sequence described in the CPC925 manual to 1518c2ecf20Sopenharmony_ci * ensure all the data gets to a point the cache incoherent 1528c2ecf20Sopenharmony_ci * DART hardware will see. 1538c2ecf20Sopenharmony_ci */ 1548c2ecf20Sopenharmony_ci asm volatile(" sync;" 1558c2ecf20Sopenharmony_ci " isync;" 1568c2ecf20Sopenharmony_ci " dcbf 0,%1;" 1578c2ecf20Sopenharmony_ci " sync;" 1588c2ecf20Sopenharmony_ci " isync;" 1598c2ecf20Sopenharmony_ci " lwz %0,0(%1);" 1608c2ecf20Sopenharmony_ci " isync" : "=r" (tmp) : "r" (end) : "memory"); 1618c2ecf20Sopenharmony_ci} 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_cistatic void dart_flush(struct iommu_table *tbl) 1648c2ecf20Sopenharmony_ci{ 1658c2ecf20Sopenharmony_ci mb(); 1668c2ecf20Sopenharmony_ci if (dart_dirty) { 1678c2ecf20Sopenharmony_ci dart_tlb_invalidate_all(); 1688c2ecf20Sopenharmony_ci dart_dirty = 0; 1698c2ecf20Sopenharmony_ci } 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistatic int dart_build(struct iommu_table *tbl, long index, 1738c2ecf20Sopenharmony_ci long npages, unsigned long uaddr, 1748c2ecf20Sopenharmony_ci enum dma_data_direction direction, 1758c2ecf20Sopenharmony_ci unsigned long attrs) 1768c2ecf20Sopenharmony_ci{ 1778c2ecf20Sopenharmony_ci unsigned int *dp, *orig_dp; 1788c2ecf20Sopenharmony_ci unsigned int rpn; 1798c2ecf20Sopenharmony_ci long l; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci DBG("dart: build at: %lx, %lx, addr: %x\n", index, npages, uaddr); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci orig_dp = dp = ((unsigned int*)tbl->it_base) + index; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci /* On U3, all memory is contiguous, so we can move this 1868c2ecf20Sopenharmony_ci * out of the loop. 1878c2ecf20Sopenharmony_ci */ 1888c2ecf20Sopenharmony_ci l = npages; 1898c2ecf20Sopenharmony_ci while (l--) { 1908c2ecf20Sopenharmony_ci rpn = __pa(uaddr) >> DART_PAGE_SHIFT; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci *(dp++) = DARTMAP_VALID | (rpn & DARTMAP_RPNMASK); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci uaddr += DART_PAGE_SIZE; 1958c2ecf20Sopenharmony_ci } 1968c2ecf20Sopenharmony_ci dart_cache_sync(orig_dp, npages); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci if (dart_is_u4) { 1998c2ecf20Sopenharmony_ci rpn = index; 2008c2ecf20Sopenharmony_ci while (npages--) 2018c2ecf20Sopenharmony_ci dart_tlb_invalidate_one(rpn++); 2028c2ecf20Sopenharmony_ci } else { 2038c2ecf20Sopenharmony_ci dart_dirty = 1; 2048c2ecf20Sopenharmony_ci } 2058c2ecf20Sopenharmony_ci return 0; 2068c2ecf20Sopenharmony_ci} 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_cistatic void dart_free(struct iommu_table *tbl, long index, long npages) 2108c2ecf20Sopenharmony_ci{ 2118c2ecf20Sopenharmony_ci unsigned int *dp, *orig_dp; 2128c2ecf20Sopenharmony_ci long orig_npages = npages; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci /* We don't worry about flushing the TLB cache. The only drawback of 2158c2ecf20Sopenharmony_ci * not doing it is that we won't catch buggy device drivers doing 2168c2ecf20Sopenharmony_ci * bad DMAs, but then no 32-bit architecture ever does either. 2178c2ecf20Sopenharmony_ci */ 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci DBG("dart: free at: %lx, %lx\n", index, npages); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci orig_dp = dp = ((unsigned int *)tbl->it_base) + index; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci while (npages--) 2248c2ecf20Sopenharmony_ci *(dp++) = dart_emptyval; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci dart_cache_sync(orig_dp, orig_npages); 2278c2ecf20Sopenharmony_ci} 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_cistatic void allocate_dart(void) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci unsigned long tmp; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci /* 512 pages (2MB) is max DART tablesize. */ 2348c2ecf20Sopenharmony_ci dart_tablesize = 1UL << 21; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci /* 2378c2ecf20Sopenharmony_ci * 16MB (1 << 24) alignment. We allocate a full 16Mb chuck since we 2388c2ecf20Sopenharmony_ci * will blow up an entire large page anyway in the kernel mapping. 2398c2ecf20Sopenharmony_ci */ 2408c2ecf20Sopenharmony_ci dart_tablebase = memblock_alloc_try_nid_raw(SZ_16M, SZ_16M, 2418c2ecf20Sopenharmony_ci MEMBLOCK_LOW_LIMIT, SZ_2G, 2428c2ecf20Sopenharmony_ci NUMA_NO_NODE); 2438c2ecf20Sopenharmony_ci if (!dart_tablebase) 2448c2ecf20Sopenharmony_ci panic("Failed to allocate 16MB below 2GB for DART table\n"); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci /* There is no point scanning the DART space for leaks*/ 2478c2ecf20Sopenharmony_ci kmemleak_no_scan((void *)dart_tablebase); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci /* Allocate a spare page to map all invalid DART pages. We need to do 2508c2ecf20Sopenharmony_ci * that to work around what looks like a problem with the HT bridge 2518c2ecf20Sopenharmony_ci * prefetching into invalid pages and corrupting data 2528c2ecf20Sopenharmony_ci */ 2538c2ecf20Sopenharmony_ci tmp = memblock_phys_alloc(DART_PAGE_SIZE, DART_PAGE_SIZE); 2548c2ecf20Sopenharmony_ci if (!tmp) 2558c2ecf20Sopenharmony_ci panic("DART: table allocation failed\n"); 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci dart_emptyval = DARTMAP_VALID | ((tmp >> DART_PAGE_SHIFT) & 2588c2ecf20Sopenharmony_ci DARTMAP_RPNMASK); 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci printk(KERN_INFO "DART table allocated at: %p\n", dart_tablebase); 2618c2ecf20Sopenharmony_ci} 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_cistatic int __init dart_init(struct device_node *dart_node) 2648c2ecf20Sopenharmony_ci{ 2658c2ecf20Sopenharmony_ci unsigned int i; 2668c2ecf20Sopenharmony_ci unsigned long base, size; 2678c2ecf20Sopenharmony_ci struct resource r; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci /* IOMMU disabled by the user ? bail out */ 2708c2ecf20Sopenharmony_ci if (iommu_is_off) 2718c2ecf20Sopenharmony_ci return -ENODEV; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci /* 2748c2ecf20Sopenharmony_ci * Only use the DART if the machine has more than 1GB of RAM 2758c2ecf20Sopenharmony_ci * or if requested with iommu=on on cmdline. 2768c2ecf20Sopenharmony_ci * 2778c2ecf20Sopenharmony_ci * 1GB of RAM is picked as limit because some default devices 2788c2ecf20Sopenharmony_ci * (i.e. Airport Extreme) have 30 bit address range limits. 2798c2ecf20Sopenharmony_ci */ 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci if (!iommu_force_on && memblock_end_of_DRAM() <= 0x40000000ull) 2828c2ecf20Sopenharmony_ci return -ENODEV; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci /* Get DART registers */ 2858c2ecf20Sopenharmony_ci if (of_address_to_resource(dart_node, 0, &r)) 2868c2ecf20Sopenharmony_ci panic("DART: can't get register base ! "); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci /* Map in DART registers */ 2898c2ecf20Sopenharmony_ci dart = ioremap(r.start, resource_size(&r)); 2908c2ecf20Sopenharmony_ci if (dart == NULL) 2918c2ecf20Sopenharmony_ci panic("DART: Cannot map registers!"); 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci /* Allocate the DART and dummy page */ 2948c2ecf20Sopenharmony_ci allocate_dart(); 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci /* Fill initial table */ 2978c2ecf20Sopenharmony_ci for (i = 0; i < dart_tablesize/4; i++) 2988c2ecf20Sopenharmony_ci dart_tablebase[i] = dart_emptyval; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci /* Push to memory */ 3018c2ecf20Sopenharmony_ci dart_cache_sync(dart_tablebase, dart_tablesize / sizeof(u32)); 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci /* Initialize DART with table base and enable it. */ 3048c2ecf20Sopenharmony_ci base = ((unsigned long)dart_tablebase) >> DART_PAGE_SHIFT; 3058c2ecf20Sopenharmony_ci size = dart_tablesize >> DART_PAGE_SHIFT; 3068c2ecf20Sopenharmony_ci if (dart_is_u4) { 3078c2ecf20Sopenharmony_ci size &= DART_SIZE_U4_SIZE_MASK; 3088c2ecf20Sopenharmony_ci DART_OUT(DART_BASE_U4, base); 3098c2ecf20Sopenharmony_ci DART_OUT(DART_SIZE_U4, size); 3108c2ecf20Sopenharmony_ci DART_OUT(DART_CNTL, DART_CNTL_U4_ENABLE); 3118c2ecf20Sopenharmony_ci } else { 3128c2ecf20Sopenharmony_ci size &= DART_CNTL_U3_SIZE_MASK; 3138c2ecf20Sopenharmony_ci DART_OUT(DART_CNTL, 3148c2ecf20Sopenharmony_ci DART_CNTL_U3_ENABLE | 3158c2ecf20Sopenharmony_ci (base << DART_CNTL_U3_BASE_SHIFT) | 3168c2ecf20Sopenharmony_ci (size << DART_CNTL_U3_SIZE_SHIFT)); 3178c2ecf20Sopenharmony_ci } 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci /* Invalidate DART to get rid of possible stale TLBs */ 3208c2ecf20Sopenharmony_ci dart_tlb_invalidate_all(); 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci printk(KERN_INFO "DART IOMMU initialized for %s type chipset\n", 3238c2ecf20Sopenharmony_ci dart_is_u4 ? "U4" : "U3"); 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci return 0; 3268c2ecf20Sopenharmony_ci} 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_cistatic struct iommu_table_ops iommu_dart_ops = { 3298c2ecf20Sopenharmony_ci .set = dart_build, 3308c2ecf20Sopenharmony_ci .clear = dart_free, 3318c2ecf20Sopenharmony_ci .flush = dart_flush, 3328c2ecf20Sopenharmony_ci}; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_cistatic void iommu_table_dart_setup(void) 3358c2ecf20Sopenharmony_ci{ 3368c2ecf20Sopenharmony_ci iommu_table_dart.it_busno = 0; 3378c2ecf20Sopenharmony_ci iommu_table_dart.it_offset = 0; 3388c2ecf20Sopenharmony_ci /* it_size is in number of entries */ 3398c2ecf20Sopenharmony_ci iommu_table_dart.it_size = dart_tablesize / sizeof(u32); 3408c2ecf20Sopenharmony_ci iommu_table_dart.it_page_shift = IOMMU_PAGE_SHIFT_4K; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci /* Initialize the common IOMMU code */ 3438c2ecf20Sopenharmony_ci iommu_table_dart.it_base = (unsigned long)dart_tablebase; 3448c2ecf20Sopenharmony_ci iommu_table_dart.it_index = 0; 3458c2ecf20Sopenharmony_ci iommu_table_dart.it_blocksize = 1; 3468c2ecf20Sopenharmony_ci iommu_table_dart.it_ops = &iommu_dart_ops; 3478c2ecf20Sopenharmony_ci iommu_init_table(&iommu_table_dart, -1, 0, 0); 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci /* Reserve the last page of the DART to avoid possible prefetch 3508c2ecf20Sopenharmony_ci * past the DART mapped area 3518c2ecf20Sopenharmony_ci */ 3528c2ecf20Sopenharmony_ci set_bit(iommu_table_dart.it_size - 1, iommu_table_dart.it_map); 3538c2ecf20Sopenharmony_ci} 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_cistatic void pci_dma_bus_setup_dart(struct pci_bus *bus) 3568c2ecf20Sopenharmony_ci{ 3578c2ecf20Sopenharmony_ci if (!iommu_table_dart_inited) { 3588c2ecf20Sopenharmony_ci iommu_table_dart_inited = 1; 3598c2ecf20Sopenharmony_ci iommu_table_dart_setup(); 3608c2ecf20Sopenharmony_ci } 3618c2ecf20Sopenharmony_ci} 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_cistatic bool dart_device_on_pcie(struct device *dev) 3648c2ecf20Sopenharmony_ci{ 3658c2ecf20Sopenharmony_ci struct device_node *np = of_node_get(dev->of_node); 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci while(np) { 3688c2ecf20Sopenharmony_ci if (of_device_is_compatible(np, "U4-pcie") || 3698c2ecf20Sopenharmony_ci of_device_is_compatible(np, "u4-pcie")) { 3708c2ecf20Sopenharmony_ci of_node_put(np); 3718c2ecf20Sopenharmony_ci return true; 3728c2ecf20Sopenharmony_ci } 3738c2ecf20Sopenharmony_ci np = of_get_next_parent(np); 3748c2ecf20Sopenharmony_ci } 3758c2ecf20Sopenharmony_ci return false; 3768c2ecf20Sopenharmony_ci} 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_cistatic void pci_dma_dev_setup_dart(struct pci_dev *dev) 3798c2ecf20Sopenharmony_ci{ 3808c2ecf20Sopenharmony_ci if (dart_is_u4 && dart_device_on_pcie(&dev->dev)) 3818c2ecf20Sopenharmony_ci dev->dev.archdata.dma_offset = DART_U4_BYPASS_BASE; 3828c2ecf20Sopenharmony_ci set_iommu_table_base(&dev->dev, &iommu_table_dart); 3838c2ecf20Sopenharmony_ci} 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_cistatic bool iommu_bypass_supported_dart(struct pci_dev *dev, u64 mask) 3868c2ecf20Sopenharmony_ci{ 3878c2ecf20Sopenharmony_ci return dart_is_u4 && 3888c2ecf20Sopenharmony_ci dart_device_on_pcie(&dev->dev) && 3898c2ecf20Sopenharmony_ci mask >= DMA_BIT_MASK(40); 3908c2ecf20Sopenharmony_ci} 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_civoid __init iommu_init_early_dart(struct pci_controller_ops *controller_ops) 3938c2ecf20Sopenharmony_ci{ 3948c2ecf20Sopenharmony_ci struct device_node *dn; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci /* Find the DART in the device-tree */ 3978c2ecf20Sopenharmony_ci dn = of_find_compatible_node(NULL, "dart", "u3-dart"); 3988c2ecf20Sopenharmony_ci if (dn == NULL) { 3998c2ecf20Sopenharmony_ci dn = of_find_compatible_node(NULL, "dart", "u4-dart"); 4008c2ecf20Sopenharmony_ci if (dn == NULL) 4018c2ecf20Sopenharmony_ci return; /* use default direct_dma_ops */ 4028c2ecf20Sopenharmony_ci dart_is_u4 = 1; 4038c2ecf20Sopenharmony_ci } 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci /* Initialize the DART HW */ 4068c2ecf20Sopenharmony_ci if (dart_init(dn) != 0) { 4078c2ecf20Sopenharmony_ci of_node_put(dn); 4088c2ecf20Sopenharmony_ci return; 4098c2ecf20Sopenharmony_ci } 4108c2ecf20Sopenharmony_ci /* 4118c2ecf20Sopenharmony_ci * U4 supports a DART bypass, we use it for 64-bit capable devices to 4128c2ecf20Sopenharmony_ci * improve performance. However, that only works for devices connected 4138c2ecf20Sopenharmony_ci * to the U4 own PCIe interface, not bridged through hypertransport. 4148c2ecf20Sopenharmony_ci * We need the device to support at least 40 bits of addresses. 4158c2ecf20Sopenharmony_ci */ 4168c2ecf20Sopenharmony_ci controller_ops->dma_dev_setup = pci_dma_dev_setup_dart; 4178c2ecf20Sopenharmony_ci controller_ops->dma_bus_setup = pci_dma_bus_setup_dart; 4188c2ecf20Sopenharmony_ci controller_ops->iommu_bypass_supported = iommu_bypass_supported_dart; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci /* Setup pci_dma ops */ 4218c2ecf20Sopenharmony_ci set_pci_dma_ops(&dma_iommu_ops); 4228c2ecf20Sopenharmony_ci of_node_put(dn); 4238c2ecf20Sopenharmony_ci} 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 4268c2ecf20Sopenharmony_cistatic void iommu_dart_restore(void) 4278c2ecf20Sopenharmony_ci{ 4288c2ecf20Sopenharmony_ci dart_cache_sync(dart_tablebase, dart_tablesize / sizeof(u32)); 4298c2ecf20Sopenharmony_ci dart_tlb_invalidate_all(); 4308c2ecf20Sopenharmony_ci} 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_cistatic int __init iommu_init_late_dart(void) 4338c2ecf20Sopenharmony_ci{ 4348c2ecf20Sopenharmony_ci if (!dart_tablebase) 4358c2ecf20Sopenharmony_ci return 0; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci ppc_md.iommu_restore = iommu_dart_restore; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci return 0; 4408c2ecf20Sopenharmony_ci} 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_cilate_initcall(iommu_init_late_dart); 4438c2ecf20Sopenharmony_ci#endif /* CONFIG_PM */ 444