18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * TCE helpers for IODA PCI/PCIe on PowerNV platforms 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2018 IBM Corp. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or 88c2ecf20Sopenharmony_ci * modify it under the terms of the GNU General Public License 98c2ecf20Sopenharmony_ci * as published by the Free Software Foundation; either version 108c2ecf20Sopenharmony_ci * 2 of the License, or (at your option) any later version. 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/kernel.h> 148c2ecf20Sopenharmony_ci#include <linux/iommu.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <asm/iommu.h> 178c2ecf20Sopenharmony_ci#include <asm/tce.h> 188c2ecf20Sopenharmony_ci#include "pci.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ciunsigned long pnv_ioda_parse_tce_sizes(struct pnv_phb *phb) 218c2ecf20Sopenharmony_ci{ 228c2ecf20Sopenharmony_ci struct pci_controller *hose = phb->hose; 238c2ecf20Sopenharmony_ci struct device_node *dn = hose->dn; 248c2ecf20Sopenharmony_ci unsigned long mask = 0; 258c2ecf20Sopenharmony_ci int i, rc, count; 268c2ecf20Sopenharmony_ci u32 val; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci count = of_property_count_u32_elems(dn, "ibm,supported-tce-sizes"); 298c2ecf20Sopenharmony_ci if (count <= 0) { 308c2ecf20Sopenharmony_ci mask = SZ_4K | SZ_64K; 318c2ecf20Sopenharmony_ci /* Add 16M for POWER8 by default */ 328c2ecf20Sopenharmony_ci if (cpu_has_feature(CPU_FTR_ARCH_207S) && 338c2ecf20Sopenharmony_ci !cpu_has_feature(CPU_FTR_ARCH_300)) 348c2ecf20Sopenharmony_ci mask |= SZ_16M | SZ_256M; 358c2ecf20Sopenharmony_ci return mask; 368c2ecf20Sopenharmony_ci } 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) { 398c2ecf20Sopenharmony_ci rc = of_property_read_u32_index(dn, "ibm,supported-tce-sizes", 408c2ecf20Sopenharmony_ci i, &val); 418c2ecf20Sopenharmony_ci if (rc == 0) 428c2ecf20Sopenharmony_ci mask |= 1ULL << val; 438c2ecf20Sopenharmony_ci } 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci return mask; 468c2ecf20Sopenharmony_ci} 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_civoid pnv_pci_setup_iommu_table(struct iommu_table *tbl, 498c2ecf20Sopenharmony_ci void *tce_mem, u64 tce_size, 508c2ecf20Sopenharmony_ci u64 dma_offset, unsigned int page_shift) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci tbl->it_blocksize = 16; 538c2ecf20Sopenharmony_ci tbl->it_base = (unsigned long)tce_mem; 548c2ecf20Sopenharmony_ci tbl->it_page_shift = page_shift; 558c2ecf20Sopenharmony_ci tbl->it_offset = dma_offset >> tbl->it_page_shift; 568c2ecf20Sopenharmony_ci tbl->it_index = 0; 578c2ecf20Sopenharmony_ci tbl->it_size = tce_size >> 3; 588c2ecf20Sopenharmony_ci tbl->it_busno = 0; 598c2ecf20Sopenharmony_ci tbl->it_type = TCE_PCI; 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic __be64 *pnv_alloc_tce_level(int nid, unsigned int shift) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci struct page *tce_mem = NULL; 658c2ecf20Sopenharmony_ci __be64 *addr; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci tce_mem = alloc_pages_node(nid, GFP_ATOMIC | __GFP_NOWARN, 688c2ecf20Sopenharmony_ci shift - PAGE_SHIFT); 698c2ecf20Sopenharmony_ci if (!tce_mem) { 708c2ecf20Sopenharmony_ci pr_err("Failed to allocate a TCE memory, level shift=%d\n", 718c2ecf20Sopenharmony_ci shift); 728c2ecf20Sopenharmony_ci return NULL; 738c2ecf20Sopenharmony_ci } 748c2ecf20Sopenharmony_ci addr = page_address(tce_mem); 758c2ecf20Sopenharmony_ci memset(addr, 0, 1UL << shift); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci return addr; 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic void pnv_pci_ioda2_table_do_free_pages(__be64 *addr, 818c2ecf20Sopenharmony_ci unsigned long size, unsigned int levels); 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic __be64 *pnv_tce(struct iommu_table *tbl, bool user, long idx, bool alloc) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci __be64 *tmp = user ? tbl->it_userspace : (__be64 *) tbl->it_base; 868c2ecf20Sopenharmony_ci int level = tbl->it_indirect_levels; 878c2ecf20Sopenharmony_ci const long shift = ilog2(tbl->it_level_size); 888c2ecf20Sopenharmony_ci unsigned long mask = (tbl->it_level_size - 1) << (level * shift); 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci while (level) { 918c2ecf20Sopenharmony_ci int n = (idx & mask) >> (level * shift); 928c2ecf20Sopenharmony_ci unsigned long oldtce, tce = be64_to_cpu(READ_ONCE(tmp[n])); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci if (!tce) { 958c2ecf20Sopenharmony_ci __be64 *tmp2; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci if (!alloc) 988c2ecf20Sopenharmony_ci return NULL; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci tmp2 = pnv_alloc_tce_level(tbl->it_nid, 1018c2ecf20Sopenharmony_ci ilog2(tbl->it_level_size) + 3); 1028c2ecf20Sopenharmony_ci if (!tmp2) 1038c2ecf20Sopenharmony_ci return NULL; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci tce = __pa(tmp2) | TCE_PCI_READ | TCE_PCI_WRITE; 1068c2ecf20Sopenharmony_ci oldtce = be64_to_cpu(cmpxchg(&tmp[n], 0, 1078c2ecf20Sopenharmony_ci cpu_to_be64(tce))); 1088c2ecf20Sopenharmony_ci if (oldtce) { 1098c2ecf20Sopenharmony_ci pnv_pci_ioda2_table_do_free_pages(tmp2, 1108c2ecf20Sopenharmony_ci ilog2(tbl->it_level_size) + 3, 1); 1118c2ecf20Sopenharmony_ci tce = oldtce; 1128c2ecf20Sopenharmony_ci } 1138c2ecf20Sopenharmony_ci } 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci tmp = __va(tce & ~(TCE_PCI_READ | TCE_PCI_WRITE)); 1168c2ecf20Sopenharmony_ci idx &= ~mask; 1178c2ecf20Sopenharmony_ci mask >>= shift; 1188c2ecf20Sopenharmony_ci --level; 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci return tmp + idx; 1228c2ecf20Sopenharmony_ci} 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ciint pnv_tce_build(struct iommu_table *tbl, long index, long npages, 1258c2ecf20Sopenharmony_ci unsigned long uaddr, enum dma_data_direction direction, 1268c2ecf20Sopenharmony_ci unsigned long attrs) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci u64 proto_tce = iommu_direction_to_tce_perm(direction); 1298c2ecf20Sopenharmony_ci u64 rpn = __pa(uaddr) >> tbl->it_page_shift; 1308c2ecf20Sopenharmony_ci long i; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci if (proto_tce & TCE_PCI_WRITE) 1338c2ecf20Sopenharmony_ci proto_tce |= TCE_PCI_READ; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci for (i = 0; i < npages; i++) { 1368c2ecf20Sopenharmony_ci unsigned long newtce = proto_tce | 1378c2ecf20Sopenharmony_ci ((rpn + i) << tbl->it_page_shift); 1388c2ecf20Sopenharmony_ci unsigned long idx = index - tbl->it_offset + i; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci *(pnv_tce(tbl, false, idx, true)) = cpu_to_be64(newtce); 1418c2ecf20Sopenharmony_ci } 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci return 0; 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci#ifdef CONFIG_IOMMU_API 1478c2ecf20Sopenharmony_ciint pnv_tce_xchg(struct iommu_table *tbl, long index, 1488c2ecf20Sopenharmony_ci unsigned long *hpa, enum dma_data_direction *direction, 1498c2ecf20Sopenharmony_ci bool alloc) 1508c2ecf20Sopenharmony_ci{ 1518c2ecf20Sopenharmony_ci u64 proto_tce = iommu_direction_to_tce_perm(*direction); 1528c2ecf20Sopenharmony_ci unsigned long newtce = *hpa | proto_tce, oldtce; 1538c2ecf20Sopenharmony_ci unsigned long idx = index - tbl->it_offset; 1548c2ecf20Sopenharmony_ci __be64 *ptce = NULL; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci BUG_ON(*hpa & ~IOMMU_PAGE_MASK(tbl)); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci if (*direction == DMA_NONE) { 1598c2ecf20Sopenharmony_ci ptce = pnv_tce(tbl, false, idx, false); 1608c2ecf20Sopenharmony_ci if (!ptce) { 1618c2ecf20Sopenharmony_ci *hpa = 0; 1628c2ecf20Sopenharmony_ci return 0; 1638c2ecf20Sopenharmony_ci } 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci if (!ptce) { 1678c2ecf20Sopenharmony_ci ptce = pnv_tce(tbl, false, idx, alloc); 1688c2ecf20Sopenharmony_ci if (!ptce) 1698c2ecf20Sopenharmony_ci return -ENOMEM; 1708c2ecf20Sopenharmony_ci } 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci if (newtce & TCE_PCI_WRITE) 1738c2ecf20Sopenharmony_ci newtce |= TCE_PCI_READ; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci oldtce = be64_to_cpu(xchg(ptce, cpu_to_be64(newtce))); 1768c2ecf20Sopenharmony_ci *hpa = oldtce & ~(TCE_PCI_READ | TCE_PCI_WRITE); 1778c2ecf20Sopenharmony_ci *direction = iommu_tce_direction(oldtce); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci return 0; 1808c2ecf20Sopenharmony_ci} 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci__be64 *pnv_tce_useraddrptr(struct iommu_table *tbl, long index, bool alloc) 1838c2ecf20Sopenharmony_ci{ 1848c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(!tbl->it_userspace)) 1858c2ecf20Sopenharmony_ci return NULL; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci return pnv_tce(tbl, true, index - tbl->it_offset, alloc); 1888c2ecf20Sopenharmony_ci} 1898c2ecf20Sopenharmony_ci#endif 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_civoid pnv_tce_free(struct iommu_table *tbl, long index, long npages) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci long i; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci for (i = 0; i < npages; i++) { 1968c2ecf20Sopenharmony_ci unsigned long idx = index - tbl->it_offset + i; 1978c2ecf20Sopenharmony_ci __be64 *ptce = pnv_tce(tbl, false, idx, false); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci if (ptce) 2008c2ecf20Sopenharmony_ci *ptce = cpu_to_be64(0); 2018c2ecf20Sopenharmony_ci else 2028c2ecf20Sopenharmony_ci /* Skip the rest of the level */ 2038c2ecf20Sopenharmony_ci i |= tbl->it_level_size - 1; 2048c2ecf20Sopenharmony_ci } 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ciunsigned long pnv_tce_get(struct iommu_table *tbl, long index) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci __be64 *ptce = pnv_tce(tbl, false, index - tbl->it_offset, false); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci if (!ptce) 2128c2ecf20Sopenharmony_ci return 0; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci return be64_to_cpu(*ptce); 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_cistatic void pnv_pci_ioda2_table_do_free_pages(__be64 *addr, 2188c2ecf20Sopenharmony_ci unsigned long size, unsigned int levels) 2198c2ecf20Sopenharmony_ci{ 2208c2ecf20Sopenharmony_ci const unsigned long addr_ul = (unsigned long) addr & 2218c2ecf20Sopenharmony_ci ~(TCE_PCI_READ | TCE_PCI_WRITE); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci if (levels) { 2248c2ecf20Sopenharmony_ci long i; 2258c2ecf20Sopenharmony_ci u64 *tmp = (u64 *) addr_ul; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci for (i = 0; i < size; ++i) { 2288c2ecf20Sopenharmony_ci unsigned long hpa = be64_to_cpu(tmp[i]); 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci if (!(hpa & (TCE_PCI_READ | TCE_PCI_WRITE))) 2318c2ecf20Sopenharmony_ci continue; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci pnv_pci_ioda2_table_do_free_pages(__va(hpa), size, 2348c2ecf20Sopenharmony_ci levels - 1); 2358c2ecf20Sopenharmony_ci } 2368c2ecf20Sopenharmony_ci } 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci free_pages(addr_ul, get_order(size << 3)); 2398c2ecf20Sopenharmony_ci} 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_civoid pnv_pci_ioda2_table_free_pages(struct iommu_table *tbl) 2428c2ecf20Sopenharmony_ci{ 2438c2ecf20Sopenharmony_ci const unsigned long size = tbl->it_indirect_levels ? 2448c2ecf20Sopenharmony_ci tbl->it_level_size : tbl->it_size; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci if (!tbl->it_size) 2478c2ecf20Sopenharmony_ci return; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci pnv_pci_ioda2_table_do_free_pages((__be64 *)tbl->it_base, size, 2508c2ecf20Sopenharmony_ci tbl->it_indirect_levels); 2518c2ecf20Sopenharmony_ci if (tbl->it_userspace) { 2528c2ecf20Sopenharmony_ci pnv_pci_ioda2_table_do_free_pages(tbl->it_userspace, size, 2538c2ecf20Sopenharmony_ci tbl->it_indirect_levels); 2548c2ecf20Sopenharmony_ci } 2558c2ecf20Sopenharmony_ci} 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_cistatic __be64 *pnv_pci_ioda2_table_do_alloc_pages(int nid, unsigned int shift, 2588c2ecf20Sopenharmony_ci unsigned int levels, unsigned long limit, 2598c2ecf20Sopenharmony_ci unsigned long *current_offset, unsigned long *total_allocated) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci __be64 *addr, *tmp; 2628c2ecf20Sopenharmony_ci unsigned long allocated = 1UL << shift; 2638c2ecf20Sopenharmony_ci unsigned int entries = 1UL << (shift - 3); 2648c2ecf20Sopenharmony_ci long i; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci addr = pnv_alloc_tce_level(nid, shift); 2678c2ecf20Sopenharmony_ci *total_allocated += allocated; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci --levels; 2708c2ecf20Sopenharmony_ci if (!levels) { 2718c2ecf20Sopenharmony_ci *current_offset += allocated; 2728c2ecf20Sopenharmony_ci return addr; 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci for (i = 0; i < entries; ++i) { 2768c2ecf20Sopenharmony_ci tmp = pnv_pci_ioda2_table_do_alloc_pages(nid, shift, 2778c2ecf20Sopenharmony_ci levels, limit, current_offset, total_allocated); 2788c2ecf20Sopenharmony_ci if (!tmp) 2798c2ecf20Sopenharmony_ci break; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci addr[i] = cpu_to_be64(__pa(tmp) | 2828c2ecf20Sopenharmony_ci TCE_PCI_READ | TCE_PCI_WRITE); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci if (*current_offset >= limit) 2858c2ecf20Sopenharmony_ci break; 2868c2ecf20Sopenharmony_ci } 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci return addr; 2898c2ecf20Sopenharmony_ci} 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_cilong pnv_pci_ioda2_table_alloc_pages(int nid, __u64 bus_offset, 2928c2ecf20Sopenharmony_ci __u32 page_shift, __u64 window_size, __u32 levels, 2938c2ecf20Sopenharmony_ci bool alloc_userspace_copy, struct iommu_table *tbl) 2948c2ecf20Sopenharmony_ci{ 2958c2ecf20Sopenharmony_ci void *addr, *uas = NULL; 2968c2ecf20Sopenharmony_ci unsigned long offset = 0, level_shift, total_allocated = 0; 2978c2ecf20Sopenharmony_ci unsigned long total_allocated_uas = 0; 2988c2ecf20Sopenharmony_ci const unsigned int window_shift = ilog2(window_size); 2998c2ecf20Sopenharmony_ci unsigned int entries_shift = window_shift - page_shift; 3008c2ecf20Sopenharmony_ci unsigned int table_shift = max_t(unsigned int, entries_shift + 3, 3018c2ecf20Sopenharmony_ci PAGE_SHIFT); 3028c2ecf20Sopenharmony_ci const unsigned long tce_table_size = 1UL << table_shift; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci if (!levels || (levels > POWERNV_IOMMU_MAX_LEVELS)) 3058c2ecf20Sopenharmony_ci return -EINVAL; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci if (!is_power_of_2(window_size)) 3088c2ecf20Sopenharmony_ci return -EINVAL; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci /* Adjust direct table size from window_size and levels */ 3118c2ecf20Sopenharmony_ci entries_shift = (entries_shift + levels - 1) / levels; 3128c2ecf20Sopenharmony_ci level_shift = entries_shift + 3; 3138c2ecf20Sopenharmony_ci level_shift = max_t(unsigned int, level_shift, PAGE_SHIFT); 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci if ((level_shift - 3) * levels + page_shift >= 55) 3168c2ecf20Sopenharmony_ci return -EINVAL; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci /* Allocate TCE table */ 3198c2ecf20Sopenharmony_ci addr = pnv_pci_ioda2_table_do_alloc_pages(nid, level_shift, 3208c2ecf20Sopenharmony_ci 1, tce_table_size, &offset, &total_allocated); 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci /* addr==NULL means that the first level allocation failed */ 3238c2ecf20Sopenharmony_ci if (!addr) 3248c2ecf20Sopenharmony_ci return -ENOMEM; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci /* 3278c2ecf20Sopenharmony_ci * First level was allocated but some lower level failed as 3288c2ecf20Sopenharmony_ci * we did not allocate as much as we wanted, 3298c2ecf20Sopenharmony_ci * release partially allocated table. 3308c2ecf20Sopenharmony_ci */ 3318c2ecf20Sopenharmony_ci if (levels == 1 && offset < tce_table_size) 3328c2ecf20Sopenharmony_ci goto free_tces_exit; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci /* Allocate userspace view of the TCE table */ 3358c2ecf20Sopenharmony_ci if (alloc_userspace_copy) { 3368c2ecf20Sopenharmony_ci offset = 0; 3378c2ecf20Sopenharmony_ci uas = pnv_pci_ioda2_table_do_alloc_pages(nid, level_shift, 3388c2ecf20Sopenharmony_ci 1, tce_table_size, &offset, 3398c2ecf20Sopenharmony_ci &total_allocated_uas); 3408c2ecf20Sopenharmony_ci if (!uas) 3418c2ecf20Sopenharmony_ci goto free_tces_exit; 3428c2ecf20Sopenharmony_ci if (levels == 1 && (offset < tce_table_size || 3438c2ecf20Sopenharmony_ci total_allocated_uas != total_allocated)) 3448c2ecf20Sopenharmony_ci goto free_uas_exit; 3458c2ecf20Sopenharmony_ci } 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci /* Setup linux iommu table */ 3488c2ecf20Sopenharmony_ci pnv_pci_setup_iommu_table(tbl, addr, tce_table_size, bus_offset, 3498c2ecf20Sopenharmony_ci page_shift); 3508c2ecf20Sopenharmony_ci tbl->it_level_size = 1ULL << (level_shift - 3); 3518c2ecf20Sopenharmony_ci tbl->it_indirect_levels = levels - 1; 3528c2ecf20Sopenharmony_ci tbl->it_userspace = uas; 3538c2ecf20Sopenharmony_ci tbl->it_nid = nid; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci pr_debug("Created TCE table: ws=%08llx ts=%lx @%08llx base=%lx uas=%p levels=%d/%d\n", 3568c2ecf20Sopenharmony_ci window_size, tce_table_size, bus_offset, tbl->it_base, 3578c2ecf20Sopenharmony_ci tbl->it_userspace, 1, levels); 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci return 0; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_cifree_uas_exit: 3628c2ecf20Sopenharmony_ci pnv_pci_ioda2_table_do_free_pages(uas, 3638c2ecf20Sopenharmony_ci 1ULL << (level_shift - 3), levels - 1); 3648c2ecf20Sopenharmony_cifree_tces_exit: 3658c2ecf20Sopenharmony_ci pnv_pci_ioda2_table_do_free_pages(addr, 3668c2ecf20Sopenharmony_ci 1ULL << (level_shift - 3), levels - 1); 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci return -ENOMEM; 3698c2ecf20Sopenharmony_ci} 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_civoid pnv_pci_unlink_table_and_group(struct iommu_table *tbl, 3728c2ecf20Sopenharmony_ci struct iommu_table_group *table_group) 3738c2ecf20Sopenharmony_ci{ 3748c2ecf20Sopenharmony_ci long i; 3758c2ecf20Sopenharmony_ci bool found; 3768c2ecf20Sopenharmony_ci struct iommu_table_group_link *tgl; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci if (!tbl || !table_group) 3798c2ecf20Sopenharmony_ci return; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci /* Remove link to a group from table's list of attached groups */ 3828c2ecf20Sopenharmony_ci found = false; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci rcu_read_lock(); 3858c2ecf20Sopenharmony_ci list_for_each_entry_rcu(tgl, &tbl->it_group_list, next) { 3868c2ecf20Sopenharmony_ci if (tgl->table_group == table_group) { 3878c2ecf20Sopenharmony_ci list_del_rcu(&tgl->next); 3888c2ecf20Sopenharmony_ci kfree_rcu(tgl, rcu); 3898c2ecf20Sopenharmony_ci found = true; 3908c2ecf20Sopenharmony_ci break; 3918c2ecf20Sopenharmony_ci } 3928c2ecf20Sopenharmony_ci } 3938c2ecf20Sopenharmony_ci rcu_read_unlock(); 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci if (WARN_ON(!found)) 3968c2ecf20Sopenharmony_ci return; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci /* Clean a pointer to iommu_table in iommu_table_group::tables[] */ 3998c2ecf20Sopenharmony_ci found = false; 4008c2ecf20Sopenharmony_ci for (i = 0; i < IOMMU_TABLE_GROUP_MAX_TABLES; ++i) { 4018c2ecf20Sopenharmony_ci if (table_group->tables[i] == tbl) { 4028c2ecf20Sopenharmony_ci iommu_tce_table_put(tbl); 4038c2ecf20Sopenharmony_ci table_group->tables[i] = NULL; 4048c2ecf20Sopenharmony_ci found = true; 4058c2ecf20Sopenharmony_ci break; 4068c2ecf20Sopenharmony_ci } 4078c2ecf20Sopenharmony_ci } 4088c2ecf20Sopenharmony_ci WARN_ON(!found); 4098c2ecf20Sopenharmony_ci} 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_cilong pnv_pci_link_table_and_group(int node, int num, 4128c2ecf20Sopenharmony_ci struct iommu_table *tbl, 4138c2ecf20Sopenharmony_ci struct iommu_table_group *table_group) 4148c2ecf20Sopenharmony_ci{ 4158c2ecf20Sopenharmony_ci struct iommu_table_group_link *tgl = NULL; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci if (WARN_ON(!tbl || !table_group)) 4188c2ecf20Sopenharmony_ci return -EINVAL; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci tgl = kzalloc_node(sizeof(struct iommu_table_group_link), GFP_KERNEL, 4218c2ecf20Sopenharmony_ci node); 4228c2ecf20Sopenharmony_ci if (!tgl) 4238c2ecf20Sopenharmony_ci return -ENOMEM; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci tgl->table_group = table_group; 4268c2ecf20Sopenharmony_ci list_add_rcu(&tgl->next, &tbl->it_group_list); 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci table_group->tables[num] = iommu_tce_table_get(tbl); 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci return 0; 4318c2ecf20Sopenharmony_ci} 432