18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * This file implements the DMA operations for NVLink devices. The NPU 48c2ecf20Sopenharmony_ci * devices all point to the same iommu table as the parent PCI device. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright Alistair Popple, IBM Corporation 2015. 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/mmu_notifier.h> 108c2ecf20Sopenharmony_ci#include <linux/mmu_context.h> 118c2ecf20Sopenharmony_ci#include <linux/of.h> 128c2ecf20Sopenharmony_ci#include <linux/pci.h> 138c2ecf20Sopenharmony_ci#include <linux/memblock.h> 148c2ecf20Sopenharmony_ci#include <linux/sizes.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <asm/debugfs.h> 178c2ecf20Sopenharmony_ci#include <asm/powernv.h> 188c2ecf20Sopenharmony_ci#include <asm/ppc-pci.h> 198c2ecf20Sopenharmony_ci#include <asm/opal.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include "pci.h" 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cistatic struct pci_dev *get_pci_dev(struct device_node *dn) 248c2ecf20Sopenharmony_ci{ 258c2ecf20Sopenharmony_ci struct pci_dn *pdn = PCI_DN(dn); 268c2ecf20Sopenharmony_ci struct pci_dev *pdev; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci pdev = pci_get_domain_bus_and_slot(pci_domain_nr(pdn->phb->bus), 298c2ecf20Sopenharmony_ci pdn->busno, pdn->devfn); 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci /* 328c2ecf20Sopenharmony_ci * pci_get_domain_bus_and_slot() increased the reference count of 338c2ecf20Sopenharmony_ci * the PCI device, but callers don't need that actually as the PE 348c2ecf20Sopenharmony_ci * already holds a reference to the device. Since callers aren't 358c2ecf20Sopenharmony_ci * aware of the reference count change, call pci_dev_put() now to 368c2ecf20Sopenharmony_ci * avoid leaks. 378c2ecf20Sopenharmony_ci */ 388c2ecf20Sopenharmony_ci if (pdev) 398c2ecf20Sopenharmony_ci pci_dev_put(pdev); 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci return pdev; 428c2ecf20Sopenharmony_ci} 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci/* Given a NPU device get the associated PCI device. */ 458c2ecf20Sopenharmony_cistruct pci_dev *pnv_pci_get_gpu_dev(struct pci_dev *npdev) 468c2ecf20Sopenharmony_ci{ 478c2ecf20Sopenharmony_ci struct device_node *dn; 488c2ecf20Sopenharmony_ci struct pci_dev *gpdev; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci if (WARN_ON(!npdev)) 518c2ecf20Sopenharmony_ci return NULL; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci if (WARN_ON(!npdev->dev.of_node)) 548c2ecf20Sopenharmony_ci return NULL; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci /* Get assoicated PCI device */ 578c2ecf20Sopenharmony_ci dn = of_parse_phandle(npdev->dev.of_node, "ibm,gpu", 0); 588c2ecf20Sopenharmony_ci if (!dn) 598c2ecf20Sopenharmony_ci return NULL; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci gpdev = get_pci_dev(dn); 628c2ecf20Sopenharmony_ci of_node_put(dn); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci return gpdev; 658c2ecf20Sopenharmony_ci} 668c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pnv_pci_get_gpu_dev); 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci/* Given the real PCI device get a linked NPU device. */ 698c2ecf20Sopenharmony_cistruct pci_dev *pnv_pci_get_npu_dev(struct pci_dev *gpdev, int index) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci struct device_node *dn; 728c2ecf20Sopenharmony_ci struct pci_dev *npdev; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci if (WARN_ON(!gpdev)) 758c2ecf20Sopenharmony_ci return NULL; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci /* Not all PCI devices have device-tree nodes */ 788c2ecf20Sopenharmony_ci if (!gpdev->dev.of_node) 798c2ecf20Sopenharmony_ci return NULL; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci /* Get assoicated PCI device */ 828c2ecf20Sopenharmony_ci dn = of_parse_phandle(gpdev->dev.of_node, "ibm,npu", index); 838c2ecf20Sopenharmony_ci if (!dn) 848c2ecf20Sopenharmony_ci return NULL; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci npdev = get_pci_dev(dn); 878c2ecf20Sopenharmony_ci of_node_put(dn); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci return npdev; 908c2ecf20Sopenharmony_ci} 918c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pnv_pci_get_npu_dev); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci#ifdef CONFIG_IOMMU_API 948c2ecf20Sopenharmony_ci/* 958c2ecf20Sopenharmony_ci * Returns the PE assoicated with the PCI device of the given 968c2ecf20Sopenharmony_ci * NPU. Returns the linked pci device if pci_dev != NULL. 978c2ecf20Sopenharmony_ci */ 988c2ecf20Sopenharmony_cistatic struct pnv_ioda_pe *get_gpu_pci_dev_and_pe(struct pnv_ioda_pe *npe, 998c2ecf20Sopenharmony_ci struct pci_dev **gpdev) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci struct pnv_phb *phb; 1028c2ecf20Sopenharmony_ci struct pci_controller *hose; 1038c2ecf20Sopenharmony_ci struct pci_dev *pdev; 1048c2ecf20Sopenharmony_ci struct pnv_ioda_pe *pe; 1058c2ecf20Sopenharmony_ci struct pci_dn *pdn; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci pdev = pnv_pci_get_gpu_dev(npe->pdev); 1088c2ecf20Sopenharmony_ci if (!pdev) 1098c2ecf20Sopenharmony_ci return NULL; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci pdn = pci_get_pdn(pdev); 1128c2ecf20Sopenharmony_ci if (WARN_ON(!pdn || pdn->pe_number == IODA_INVALID_PE)) 1138c2ecf20Sopenharmony_ci return NULL; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci hose = pci_bus_to_host(pdev->bus); 1168c2ecf20Sopenharmony_ci phb = hose->private_data; 1178c2ecf20Sopenharmony_ci pe = &phb->ioda.pe_array[pdn->pe_number]; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci if (gpdev) 1208c2ecf20Sopenharmony_ci *gpdev = pdev; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci return pe; 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistatic long pnv_npu_unset_window(struct iommu_table_group *table_group, 1268c2ecf20Sopenharmony_ci int num); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic long pnv_npu_set_window(struct iommu_table_group *table_group, int num, 1298c2ecf20Sopenharmony_ci struct iommu_table *tbl) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci struct pnv_ioda_pe *npe = container_of(table_group, struct pnv_ioda_pe, 1328c2ecf20Sopenharmony_ci table_group); 1338c2ecf20Sopenharmony_ci struct pnv_phb *phb = npe->phb; 1348c2ecf20Sopenharmony_ci int64_t rc; 1358c2ecf20Sopenharmony_ci const unsigned long size = tbl->it_indirect_levels ? 1368c2ecf20Sopenharmony_ci tbl->it_level_size : tbl->it_size; 1378c2ecf20Sopenharmony_ci const __u64 start_addr = tbl->it_offset << tbl->it_page_shift; 1388c2ecf20Sopenharmony_ci const __u64 win_size = tbl->it_size << tbl->it_page_shift; 1398c2ecf20Sopenharmony_ci int num2 = (num == 0) ? 1 : 0; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci /* NPU has just one TVE so if there is another table, remove it first */ 1428c2ecf20Sopenharmony_ci if (npe->table_group.tables[num2]) 1438c2ecf20Sopenharmony_ci pnv_npu_unset_window(&npe->table_group, num2); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci pe_info(npe, "Setting up window %llx..%llx pg=%lx\n", 1468c2ecf20Sopenharmony_ci start_addr, start_addr + win_size - 1, 1478c2ecf20Sopenharmony_ci IOMMU_PAGE_SIZE(tbl)); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci rc = opal_pci_map_pe_dma_window(phb->opal_id, 1508c2ecf20Sopenharmony_ci npe->pe_number, 1518c2ecf20Sopenharmony_ci npe->pe_number, 1528c2ecf20Sopenharmony_ci tbl->it_indirect_levels + 1, 1538c2ecf20Sopenharmony_ci __pa(tbl->it_base), 1548c2ecf20Sopenharmony_ci size << 3, 1558c2ecf20Sopenharmony_ci IOMMU_PAGE_SIZE(tbl)); 1568c2ecf20Sopenharmony_ci if (rc) { 1578c2ecf20Sopenharmony_ci pe_err(npe, "Failed to configure TCE table, err %lld\n", rc); 1588c2ecf20Sopenharmony_ci return rc; 1598c2ecf20Sopenharmony_ci } 1608c2ecf20Sopenharmony_ci pnv_pci_ioda2_tce_invalidate_entire(phb, false); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci /* Add the table to the list so its TCE cache will get invalidated */ 1638c2ecf20Sopenharmony_ci pnv_pci_link_table_and_group(phb->hose->node, num, 1648c2ecf20Sopenharmony_ci tbl, &npe->table_group); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci return 0; 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistatic long pnv_npu_unset_window(struct iommu_table_group *table_group, int num) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci struct pnv_ioda_pe *npe = container_of(table_group, struct pnv_ioda_pe, 1728c2ecf20Sopenharmony_ci table_group); 1738c2ecf20Sopenharmony_ci struct pnv_phb *phb = npe->phb; 1748c2ecf20Sopenharmony_ci int64_t rc; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci if (!npe->table_group.tables[num]) 1778c2ecf20Sopenharmony_ci return 0; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci pe_info(npe, "Removing DMA window\n"); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci rc = opal_pci_map_pe_dma_window(phb->opal_id, npe->pe_number, 1828c2ecf20Sopenharmony_ci npe->pe_number, 1838c2ecf20Sopenharmony_ci 0/* levels */, 0/* table address */, 1848c2ecf20Sopenharmony_ci 0/* table size */, 0/* page size */); 1858c2ecf20Sopenharmony_ci if (rc) { 1868c2ecf20Sopenharmony_ci pe_err(npe, "Unmapping failed, ret = %lld\n", rc); 1878c2ecf20Sopenharmony_ci return rc; 1888c2ecf20Sopenharmony_ci } 1898c2ecf20Sopenharmony_ci pnv_pci_ioda2_tce_invalidate_entire(phb, false); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci pnv_pci_unlink_table_and_group(npe->table_group.tables[num], 1928c2ecf20Sopenharmony_ci &npe->table_group); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci return 0; 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci/* Switch ownership from platform code to external user (e.g. VFIO) */ 1988c2ecf20Sopenharmony_cistatic void pnv_npu_take_ownership(struct iommu_table_group *table_group) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci struct pnv_ioda_pe *npe = container_of(table_group, struct pnv_ioda_pe, 2018c2ecf20Sopenharmony_ci table_group); 2028c2ecf20Sopenharmony_ci struct pnv_phb *phb = npe->phb; 2038c2ecf20Sopenharmony_ci int64_t rc; 2048c2ecf20Sopenharmony_ci struct pci_dev *gpdev = NULL; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci /* 2078c2ecf20Sopenharmony_ci * Note: NPU has just a single TVE in the hardware which means that 2088c2ecf20Sopenharmony_ci * while used by the kernel, it can have either 32bit window or 2098c2ecf20Sopenharmony_ci * DMA bypass but never both. So we deconfigure 32bit window only 2108c2ecf20Sopenharmony_ci * if it was enabled at the moment of ownership change. 2118c2ecf20Sopenharmony_ci */ 2128c2ecf20Sopenharmony_ci if (npe->table_group.tables[0]) { 2138c2ecf20Sopenharmony_ci pnv_npu_unset_window(&npe->table_group, 0); 2148c2ecf20Sopenharmony_ci return; 2158c2ecf20Sopenharmony_ci } 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci /* Disable bypass */ 2188c2ecf20Sopenharmony_ci rc = opal_pci_map_pe_dma_window_real(phb->opal_id, 2198c2ecf20Sopenharmony_ci npe->pe_number, npe->pe_number, 2208c2ecf20Sopenharmony_ci 0 /* bypass base */, 0); 2218c2ecf20Sopenharmony_ci if (rc) { 2228c2ecf20Sopenharmony_ci pe_err(npe, "Failed to disable bypass, err %lld\n", rc); 2238c2ecf20Sopenharmony_ci return; 2248c2ecf20Sopenharmony_ci } 2258c2ecf20Sopenharmony_ci pnv_pci_ioda2_tce_invalidate_entire(npe->phb, false); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci get_gpu_pci_dev_and_pe(npe, &gpdev); 2288c2ecf20Sopenharmony_ci if (gpdev) 2298c2ecf20Sopenharmony_ci pnv_npu2_unmap_lpar_dev(gpdev); 2308c2ecf20Sopenharmony_ci} 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_cistatic void pnv_npu_release_ownership(struct iommu_table_group *table_group) 2338c2ecf20Sopenharmony_ci{ 2348c2ecf20Sopenharmony_ci struct pnv_ioda_pe *npe = container_of(table_group, struct pnv_ioda_pe, 2358c2ecf20Sopenharmony_ci table_group); 2368c2ecf20Sopenharmony_ci struct pci_dev *gpdev = NULL; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci get_gpu_pci_dev_and_pe(npe, &gpdev); 2398c2ecf20Sopenharmony_ci if (gpdev) 2408c2ecf20Sopenharmony_ci pnv_npu2_map_lpar_dev(gpdev, 0, MSR_DR | MSR_PR | MSR_HV); 2418c2ecf20Sopenharmony_ci} 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_cistatic struct iommu_table_group_ops pnv_pci_npu_ops = { 2448c2ecf20Sopenharmony_ci .set_window = pnv_npu_set_window, 2458c2ecf20Sopenharmony_ci .unset_window = pnv_npu_unset_window, 2468c2ecf20Sopenharmony_ci .take_ownership = pnv_npu_take_ownership, 2478c2ecf20Sopenharmony_ci .release_ownership = pnv_npu_release_ownership, 2488c2ecf20Sopenharmony_ci}; 2498c2ecf20Sopenharmony_ci#endif /* !CONFIG_IOMMU_API */ 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci/* 2528c2ecf20Sopenharmony_ci * NPU2 ATS 2538c2ecf20Sopenharmony_ci */ 2548c2ecf20Sopenharmony_ci/* Maximum possible number of ATSD MMIO registers per NPU */ 2558c2ecf20Sopenharmony_ci#define NV_NMMU_ATSD_REGS 8 2568c2ecf20Sopenharmony_ci#define NV_NPU_MAX_PE_NUM 16 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci/* 2598c2ecf20Sopenharmony_ci * A compound NPU IOMMU group which might consist of 1 GPU + 2xNPUs (POWER8) or 2608c2ecf20Sopenharmony_ci * up to 3 x (GPU + 2xNPUs) (POWER9). 2618c2ecf20Sopenharmony_ci */ 2628c2ecf20Sopenharmony_cistruct npu_comp { 2638c2ecf20Sopenharmony_ci struct iommu_table_group table_group; 2648c2ecf20Sopenharmony_ci int pe_num; 2658c2ecf20Sopenharmony_ci struct pnv_ioda_pe *pe[NV_NPU_MAX_PE_NUM]; 2668c2ecf20Sopenharmony_ci}; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci/* An NPU descriptor, valid for POWER9 only */ 2698c2ecf20Sopenharmony_cistruct npu { 2708c2ecf20Sopenharmony_ci int index; 2718c2ecf20Sopenharmony_ci struct npu_comp npucomp; 2728c2ecf20Sopenharmony_ci}; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci#ifdef CONFIG_IOMMU_API 2758c2ecf20Sopenharmony_cistatic long pnv_npu_peers_create_table_userspace( 2768c2ecf20Sopenharmony_ci struct iommu_table_group *table_group, 2778c2ecf20Sopenharmony_ci int num, __u32 page_shift, __u64 window_size, __u32 levels, 2788c2ecf20Sopenharmony_ci struct iommu_table **ptbl) 2798c2ecf20Sopenharmony_ci{ 2808c2ecf20Sopenharmony_ci struct npu_comp *npucomp = container_of(table_group, struct npu_comp, 2818c2ecf20Sopenharmony_ci table_group); 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci if (!npucomp->pe_num || !npucomp->pe[0] || 2848c2ecf20Sopenharmony_ci !npucomp->pe[0]->table_group.ops || 2858c2ecf20Sopenharmony_ci !npucomp->pe[0]->table_group.ops->create_table) 2868c2ecf20Sopenharmony_ci return -EFAULT; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci return npucomp->pe[0]->table_group.ops->create_table( 2898c2ecf20Sopenharmony_ci &npucomp->pe[0]->table_group, num, page_shift, 2908c2ecf20Sopenharmony_ci window_size, levels, ptbl); 2918c2ecf20Sopenharmony_ci} 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_cistatic long pnv_npu_peers_set_window(struct iommu_table_group *table_group, 2948c2ecf20Sopenharmony_ci int num, struct iommu_table *tbl) 2958c2ecf20Sopenharmony_ci{ 2968c2ecf20Sopenharmony_ci int i, j; 2978c2ecf20Sopenharmony_ci long ret = 0; 2988c2ecf20Sopenharmony_ci struct npu_comp *npucomp = container_of(table_group, struct npu_comp, 2998c2ecf20Sopenharmony_ci table_group); 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci for (i = 0; i < npucomp->pe_num; ++i) { 3028c2ecf20Sopenharmony_ci struct pnv_ioda_pe *pe = npucomp->pe[i]; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci if (!pe->table_group.ops->set_window) 3058c2ecf20Sopenharmony_ci continue; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci ret = pe->table_group.ops->set_window(&pe->table_group, 3088c2ecf20Sopenharmony_ci num, tbl); 3098c2ecf20Sopenharmony_ci if (ret) 3108c2ecf20Sopenharmony_ci break; 3118c2ecf20Sopenharmony_ci } 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci if (ret) { 3148c2ecf20Sopenharmony_ci for (j = 0; j < i; ++j) { 3158c2ecf20Sopenharmony_ci struct pnv_ioda_pe *pe = npucomp->pe[j]; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci if (!pe->table_group.ops->unset_window) 3188c2ecf20Sopenharmony_ci continue; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci ret = pe->table_group.ops->unset_window( 3218c2ecf20Sopenharmony_ci &pe->table_group, num); 3228c2ecf20Sopenharmony_ci if (ret) 3238c2ecf20Sopenharmony_ci break; 3248c2ecf20Sopenharmony_ci } 3258c2ecf20Sopenharmony_ci } else { 3268c2ecf20Sopenharmony_ci table_group->tables[num] = iommu_tce_table_get(tbl); 3278c2ecf20Sopenharmony_ci } 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci return ret; 3308c2ecf20Sopenharmony_ci} 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_cistatic long pnv_npu_peers_unset_window(struct iommu_table_group *table_group, 3338c2ecf20Sopenharmony_ci int num) 3348c2ecf20Sopenharmony_ci{ 3358c2ecf20Sopenharmony_ci int i, j; 3368c2ecf20Sopenharmony_ci long ret = 0; 3378c2ecf20Sopenharmony_ci struct npu_comp *npucomp = container_of(table_group, struct npu_comp, 3388c2ecf20Sopenharmony_ci table_group); 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci for (i = 0; i < npucomp->pe_num; ++i) { 3418c2ecf20Sopenharmony_ci struct pnv_ioda_pe *pe = npucomp->pe[i]; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci WARN_ON(npucomp->table_group.tables[num] != 3448c2ecf20Sopenharmony_ci table_group->tables[num]); 3458c2ecf20Sopenharmony_ci if (!npucomp->table_group.tables[num]) 3468c2ecf20Sopenharmony_ci continue; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci if (!pe->table_group.ops->unset_window) 3498c2ecf20Sopenharmony_ci continue; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci ret = pe->table_group.ops->unset_window(&pe->table_group, num); 3528c2ecf20Sopenharmony_ci if (ret) 3538c2ecf20Sopenharmony_ci break; 3548c2ecf20Sopenharmony_ci } 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci if (ret) { 3578c2ecf20Sopenharmony_ci for (j = 0; j < i; ++j) { 3588c2ecf20Sopenharmony_ci struct pnv_ioda_pe *pe = npucomp->pe[j]; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci if (!npucomp->table_group.tables[num]) 3618c2ecf20Sopenharmony_ci continue; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci if (!pe->table_group.ops->set_window) 3648c2ecf20Sopenharmony_ci continue; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci ret = pe->table_group.ops->set_window(&pe->table_group, 3678c2ecf20Sopenharmony_ci num, table_group->tables[num]); 3688c2ecf20Sopenharmony_ci if (ret) 3698c2ecf20Sopenharmony_ci break; 3708c2ecf20Sopenharmony_ci } 3718c2ecf20Sopenharmony_ci } else if (table_group->tables[num]) { 3728c2ecf20Sopenharmony_ci iommu_tce_table_put(table_group->tables[num]); 3738c2ecf20Sopenharmony_ci table_group->tables[num] = NULL; 3748c2ecf20Sopenharmony_ci } 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci return ret; 3778c2ecf20Sopenharmony_ci} 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_cistatic void pnv_npu_peers_take_ownership(struct iommu_table_group *table_group) 3808c2ecf20Sopenharmony_ci{ 3818c2ecf20Sopenharmony_ci int i; 3828c2ecf20Sopenharmony_ci struct npu_comp *npucomp = container_of(table_group, struct npu_comp, 3838c2ecf20Sopenharmony_ci table_group); 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci for (i = 0; i < npucomp->pe_num; ++i) { 3868c2ecf20Sopenharmony_ci struct pnv_ioda_pe *pe = npucomp->pe[i]; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci if (!pe->table_group.ops || 3898c2ecf20Sopenharmony_ci !pe->table_group.ops->take_ownership) 3908c2ecf20Sopenharmony_ci continue; 3918c2ecf20Sopenharmony_ci pe->table_group.ops->take_ownership(&pe->table_group); 3928c2ecf20Sopenharmony_ci } 3938c2ecf20Sopenharmony_ci} 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_cistatic void pnv_npu_peers_release_ownership( 3968c2ecf20Sopenharmony_ci struct iommu_table_group *table_group) 3978c2ecf20Sopenharmony_ci{ 3988c2ecf20Sopenharmony_ci int i; 3998c2ecf20Sopenharmony_ci struct npu_comp *npucomp = container_of(table_group, struct npu_comp, 4008c2ecf20Sopenharmony_ci table_group); 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci for (i = 0; i < npucomp->pe_num; ++i) { 4038c2ecf20Sopenharmony_ci struct pnv_ioda_pe *pe = npucomp->pe[i]; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci if (!pe->table_group.ops || 4068c2ecf20Sopenharmony_ci !pe->table_group.ops->release_ownership) 4078c2ecf20Sopenharmony_ci continue; 4088c2ecf20Sopenharmony_ci pe->table_group.ops->release_ownership(&pe->table_group); 4098c2ecf20Sopenharmony_ci } 4108c2ecf20Sopenharmony_ci} 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_cistatic struct iommu_table_group_ops pnv_npu_peers_ops = { 4138c2ecf20Sopenharmony_ci .get_table_size = pnv_pci_ioda2_get_table_size, 4148c2ecf20Sopenharmony_ci .create_table = pnv_npu_peers_create_table_userspace, 4158c2ecf20Sopenharmony_ci .set_window = pnv_npu_peers_set_window, 4168c2ecf20Sopenharmony_ci .unset_window = pnv_npu_peers_unset_window, 4178c2ecf20Sopenharmony_ci .take_ownership = pnv_npu_peers_take_ownership, 4188c2ecf20Sopenharmony_ci .release_ownership = pnv_npu_peers_release_ownership, 4198c2ecf20Sopenharmony_ci}; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_cistatic void pnv_comp_attach_table_group(struct npu_comp *npucomp, 4228c2ecf20Sopenharmony_ci struct pnv_ioda_pe *pe) 4238c2ecf20Sopenharmony_ci{ 4248c2ecf20Sopenharmony_ci if (WARN_ON(npucomp->pe_num == NV_NPU_MAX_PE_NUM)) 4258c2ecf20Sopenharmony_ci return; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci npucomp->pe[npucomp->pe_num] = pe; 4288c2ecf20Sopenharmony_ci ++npucomp->pe_num; 4298c2ecf20Sopenharmony_ci} 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_cistatic struct iommu_table_group * 4328c2ecf20Sopenharmony_ci pnv_try_setup_npu_table_group(struct pnv_ioda_pe *pe) 4338c2ecf20Sopenharmony_ci{ 4348c2ecf20Sopenharmony_ci struct iommu_table_group *compound_group; 4358c2ecf20Sopenharmony_ci struct npu_comp *npucomp; 4368c2ecf20Sopenharmony_ci struct pci_dev *gpdev = NULL; 4378c2ecf20Sopenharmony_ci struct pci_controller *hose; 4388c2ecf20Sopenharmony_ci struct pci_dev *npdev = NULL; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci list_for_each_entry(gpdev, &pe->pbus->devices, bus_list) { 4418c2ecf20Sopenharmony_ci npdev = pnv_pci_get_npu_dev(gpdev, 0); 4428c2ecf20Sopenharmony_ci if (npdev) 4438c2ecf20Sopenharmony_ci break; 4448c2ecf20Sopenharmony_ci } 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci if (!npdev) 4478c2ecf20Sopenharmony_ci /* It is not an NPU attached device, skip */ 4488c2ecf20Sopenharmony_ci return NULL; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci hose = pci_bus_to_host(npdev->bus); 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci if (hose->npu) { 4538c2ecf20Sopenharmony_ci /* P9 case: compound group is per-NPU (all gpus, all links) */ 4548c2ecf20Sopenharmony_ci npucomp = &hose->npu->npucomp; 4558c2ecf20Sopenharmony_ci } else { 4568c2ecf20Sopenharmony_ci /* P8 case: Compound group is per-GPU (1 gpu, 2 links) */ 4578c2ecf20Sopenharmony_ci npucomp = pe->npucomp = kzalloc(sizeof(*npucomp), GFP_KERNEL); 4588c2ecf20Sopenharmony_ci } 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci compound_group = &npucomp->table_group; 4618c2ecf20Sopenharmony_ci if (!compound_group->group) { 4628c2ecf20Sopenharmony_ci compound_group->ops = &pnv_npu_peers_ops; 4638c2ecf20Sopenharmony_ci iommu_register_group(compound_group, hose->global_number, 4648c2ecf20Sopenharmony_ci pe->pe_number); 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci /* Steal capabilities from a GPU PE */ 4678c2ecf20Sopenharmony_ci compound_group->max_dynamic_windows_supported = 4688c2ecf20Sopenharmony_ci pe->table_group.max_dynamic_windows_supported; 4698c2ecf20Sopenharmony_ci compound_group->tce32_start = pe->table_group.tce32_start; 4708c2ecf20Sopenharmony_ci compound_group->tce32_size = pe->table_group.tce32_size; 4718c2ecf20Sopenharmony_ci compound_group->max_levels = pe->table_group.max_levels; 4728c2ecf20Sopenharmony_ci if (!compound_group->pgsizes) 4738c2ecf20Sopenharmony_ci compound_group->pgsizes = pe->table_group.pgsizes; 4748c2ecf20Sopenharmony_ci } 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci /* 4778c2ecf20Sopenharmony_ci * The gpu would have been added to the iommu group that's created 4788c2ecf20Sopenharmony_ci * for the PE. Pull it out now. 4798c2ecf20Sopenharmony_ci */ 4808c2ecf20Sopenharmony_ci iommu_del_device(&gpdev->dev); 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci /* 4838c2ecf20Sopenharmony_ci * I'm not sure this is strictly required, but it's probably a good idea 4848c2ecf20Sopenharmony_ci * since the table_group for the PE is going to be attached to the 4858c2ecf20Sopenharmony_ci * compound table group. If we leave the PE's iommu group active then 4868c2ecf20Sopenharmony_ci * we might have the same table_group being modifiable via two sepeate 4878c2ecf20Sopenharmony_ci * iommu groups. 4888c2ecf20Sopenharmony_ci */ 4898c2ecf20Sopenharmony_ci iommu_group_put(pe->table_group.group); 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci /* now put the GPU into the compound group */ 4928c2ecf20Sopenharmony_ci pnv_comp_attach_table_group(npucomp, pe); 4938c2ecf20Sopenharmony_ci iommu_add_device(compound_group, &gpdev->dev); 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci return compound_group; 4968c2ecf20Sopenharmony_ci} 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_cistatic struct iommu_table_group *pnv_npu_compound_attach(struct pnv_ioda_pe *pe) 4998c2ecf20Sopenharmony_ci{ 5008c2ecf20Sopenharmony_ci struct iommu_table_group *table_group; 5018c2ecf20Sopenharmony_ci struct npu_comp *npucomp; 5028c2ecf20Sopenharmony_ci struct pci_dev *gpdev = NULL; 5038c2ecf20Sopenharmony_ci struct pci_dev *npdev; 5048c2ecf20Sopenharmony_ci struct pnv_ioda_pe *gpe = get_gpu_pci_dev_and_pe(pe, &gpdev); 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci WARN_ON(!(pe->flags & PNV_IODA_PE_DEV)); 5078c2ecf20Sopenharmony_ci if (!gpe) 5088c2ecf20Sopenharmony_ci return NULL; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci /* 5118c2ecf20Sopenharmony_ci * IODA2 bridges get this set up from pci_controller_ops::setup_bridge 5128c2ecf20Sopenharmony_ci * but NPU bridges do not have this hook defined so we do it here. 5138c2ecf20Sopenharmony_ci * We do not setup other table group parameters as they won't be used 5148c2ecf20Sopenharmony_ci * anyway - NVLink bridges are subordinate PEs. 5158c2ecf20Sopenharmony_ci */ 5168c2ecf20Sopenharmony_ci pe->table_group.ops = &pnv_pci_npu_ops; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci table_group = iommu_group_get_iommudata( 5198c2ecf20Sopenharmony_ci iommu_group_get(&gpdev->dev)); 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci /* 5228c2ecf20Sopenharmony_ci * On P9 NPU PHB and PCI PHB support different page sizes, 5238c2ecf20Sopenharmony_ci * keep only matching. We expect here that NVLink bridge PE pgsizes is 5248c2ecf20Sopenharmony_ci * initialized by the caller. 5258c2ecf20Sopenharmony_ci */ 5268c2ecf20Sopenharmony_ci table_group->pgsizes &= pe->table_group.pgsizes; 5278c2ecf20Sopenharmony_ci npucomp = container_of(table_group, struct npu_comp, table_group); 5288c2ecf20Sopenharmony_ci pnv_comp_attach_table_group(npucomp, pe); 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci list_for_each_entry(npdev, &pe->phb->hose->bus->devices, bus_list) { 5318c2ecf20Sopenharmony_ci struct pci_dev *gpdevtmp = pnv_pci_get_gpu_dev(npdev); 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci if (gpdevtmp != gpdev) 5348c2ecf20Sopenharmony_ci continue; 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci iommu_add_device(table_group, &npdev->dev); 5378c2ecf20Sopenharmony_ci } 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci return table_group; 5408c2ecf20Sopenharmony_ci} 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_civoid pnv_pci_npu_setup_iommu_groups(void) 5438c2ecf20Sopenharmony_ci{ 5448c2ecf20Sopenharmony_ci struct pci_controller *hose; 5458c2ecf20Sopenharmony_ci struct pnv_phb *phb; 5468c2ecf20Sopenharmony_ci struct pnv_ioda_pe *pe; 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci /* 5498c2ecf20Sopenharmony_ci * For non-nvlink devices the IOMMU group is registered when the PE is 5508c2ecf20Sopenharmony_ci * configured and devices are added to the group when the per-device 5518c2ecf20Sopenharmony_ci * DMA setup is run. That's done in hose->ops.dma_dev_setup() which is 5528c2ecf20Sopenharmony_ci * only initialise for "normal" IODA PHBs. 5538c2ecf20Sopenharmony_ci * 5548c2ecf20Sopenharmony_ci * For NVLink devices we need to ensure the NVLinks and the GPU end up 5558c2ecf20Sopenharmony_ci * in the same IOMMU group, so that's handled here. 5568c2ecf20Sopenharmony_ci */ 5578c2ecf20Sopenharmony_ci list_for_each_entry(hose, &hose_list, list_node) { 5588c2ecf20Sopenharmony_ci phb = hose->private_data; 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci if (phb->type == PNV_PHB_IODA2) 5618c2ecf20Sopenharmony_ci list_for_each_entry(pe, &phb->ioda.pe_list, list) 5628c2ecf20Sopenharmony_ci pnv_try_setup_npu_table_group(pe); 5638c2ecf20Sopenharmony_ci } 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci /* 5668c2ecf20Sopenharmony_ci * Now we have all PHBs discovered, time to add NPU devices to 5678c2ecf20Sopenharmony_ci * the corresponding IOMMU groups. 5688c2ecf20Sopenharmony_ci */ 5698c2ecf20Sopenharmony_ci list_for_each_entry(hose, &hose_list, list_node) { 5708c2ecf20Sopenharmony_ci unsigned long pgsizes; 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci phb = hose->private_data; 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci if (phb->type != PNV_PHB_NPU_NVLINK) 5758c2ecf20Sopenharmony_ci continue; 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci pgsizes = pnv_ioda_parse_tce_sizes(phb); 5788c2ecf20Sopenharmony_ci list_for_each_entry(pe, &phb->ioda.pe_list, list) { 5798c2ecf20Sopenharmony_ci /* 5808c2ecf20Sopenharmony_ci * IODA2 bridges get this set up from 5818c2ecf20Sopenharmony_ci * pci_controller_ops::setup_bridge but NPU bridges 5828c2ecf20Sopenharmony_ci * do not have this hook defined so we do it here. 5838c2ecf20Sopenharmony_ci */ 5848c2ecf20Sopenharmony_ci pe->table_group.pgsizes = pgsizes; 5858c2ecf20Sopenharmony_ci pnv_npu_compound_attach(pe); 5868c2ecf20Sopenharmony_ci } 5878c2ecf20Sopenharmony_ci } 5888c2ecf20Sopenharmony_ci} 5898c2ecf20Sopenharmony_ci#endif /* CONFIG_IOMMU_API */ 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ciint pnv_npu2_init(struct pci_controller *hose) 5928c2ecf20Sopenharmony_ci{ 5938c2ecf20Sopenharmony_ci static int npu_index; 5948c2ecf20Sopenharmony_ci struct npu *npu; 5958c2ecf20Sopenharmony_ci int ret; 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci npu = kzalloc(sizeof(*npu), GFP_KERNEL); 5988c2ecf20Sopenharmony_ci if (!npu) 5998c2ecf20Sopenharmony_ci return -ENOMEM; 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci npu_index++; 6028c2ecf20Sopenharmony_ci if (WARN_ON(npu_index >= NV_MAX_NPUS)) { 6038c2ecf20Sopenharmony_ci ret = -ENOSPC; 6048c2ecf20Sopenharmony_ci goto fail_exit; 6058c2ecf20Sopenharmony_ci } 6068c2ecf20Sopenharmony_ci npu->index = npu_index; 6078c2ecf20Sopenharmony_ci hose->npu = npu; 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci return 0; 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_cifail_exit: 6128c2ecf20Sopenharmony_ci kfree(npu); 6138c2ecf20Sopenharmony_ci return ret; 6148c2ecf20Sopenharmony_ci} 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ciint pnv_npu2_map_lpar_dev(struct pci_dev *gpdev, unsigned int lparid, 6178c2ecf20Sopenharmony_ci unsigned long msr) 6188c2ecf20Sopenharmony_ci{ 6198c2ecf20Sopenharmony_ci int ret; 6208c2ecf20Sopenharmony_ci struct pci_dev *npdev = pnv_pci_get_npu_dev(gpdev, 0); 6218c2ecf20Sopenharmony_ci struct pci_controller *hose; 6228c2ecf20Sopenharmony_ci struct pnv_phb *nphb; 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci if (!npdev) 6258c2ecf20Sopenharmony_ci return -ENODEV; 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci hose = pci_bus_to_host(npdev->bus); 6288c2ecf20Sopenharmony_ci if (hose->npu == NULL) { 6298c2ecf20Sopenharmony_ci dev_info_once(&npdev->dev, "Nvlink1 does not support contexts"); 6308c2ecf20Sopenharmony_ci return 0; 6318c2ecf20Sopenharmony_ci } 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci nphb = hose->private_data; 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci dev_dbg(&gpdev->dev, "Map LPAR opalid=%llu lparid=%u\n", 6368c2ecf20Sopenharmony_ci nphb->opal_id, lparid); 6378c2ecf20Sopenharmony_ci /* 6388c2ecf20Sopenharmony_ci * Currently we only support radix and non-zero LPCR only makes sense 6398c2ecf20Sopenharmony_ci * for hash tables so skiboot expects the LPCR parameter to be a zero. 6408c2ecf20Sopenharmony_ci */ 6418c2ecf20Sopenharmony_ci ret = opal_npu_map_lpar(nphb->opal_id, pci_dev_id(gpdev), lparid, 6428c2ecf20Sopenharmony_ci 0 /* LPCR bits */); 6438c2ecf20Sopenharmony_ci if (ret) { 6448c2ecf20Sopenharmony_ci dev_err(&gpdev->dev, "Error %d mapping device to LPAR\n", ret); 6458c2ecf20Sopenharmony_ci return ret; 6468c2ecf20Sopenharmony_ci } 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci dev_dbg(&gpdev->dev, "init context opalid=%llu msr=%lx\n", 6498c2ecf20Sopenharmony_ci nphb->opal_id, msr); 6508c2ecf20Sopenharmony_ci ret = opal_npu_init_context(nphb->opal_id, 0/*__unused*/, msr, 6518c2ecf20Sopenharmony_ci pci_dev_id(gpdev)); 6528c2ecf20Sopenharmony_ci if (ret < 0) 6538c2ecf20Sopenharmony_ci dev_err(&gpdev->dev, "Failed to init context: %d\n", ret); 6548c2ecf20Sopenharmony_ci else 6558c2ecf20Sopenharmony_ci ret = 0; 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci return 0; 6588c2ecf20Sopenharmony_ci} 6598c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(pnv_npu2_map_lpar_dev); 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_civoid pnv_npu2_map_lpar(struct pnv_ioda_pe *gpe, unsigned long msr) 6628c2ecf20Sopenharmony_ci{ 6638c2ecf20Sopenharmony_ci struct pci_dev *gpdev; 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci list_for_each_entry(gpdev, &gpe->pbus->devices, bus_list) 6668c2ecf20Sopenharmony_ci pnv_npu2_map_lpar_dev(gpdev, 0, msr); 6678c2ecf20Sopenharmony_ci} 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ciint pnv_npu2_unmap_lpar_dev(struct pci_dev *gpdev) 6708c2ecf20Sopenharmony_ci{ 6718c2ecf20Sopenharmony_ci int ret; 6728c2ecf20Sopenharmony_ci struct pci_dev *npdev = pnv_pci_get_npu_dev(gpdev, 0); 6738c2ecf20Sopenharmony_ci struct pci_controller *hose; 6748c2ecf20Sopenharmony_ci struct pnv_phb *nphb; 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci if (!npdev) 6778c2ecf20Sopenharmony_ci return -ENODEV; 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci hose = pci_bus_to_host(npdev->bus); 6808c2ecf20Sopenharmony_ci if (hose->npu == NULL) { 6818c2ecf20Sopenharmony_ci dev_info_once(&npdev->dev, "Nvlink1 does not support contexts"); 6828c2ecf20Sopenharmony_ci return 0; 6838c2ecf20Sopenharmony_ci } 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci nphb = hose->private_data; 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci dev_dbg(&gpdev->dev, "destroy context opalid=%llu\n", 6888c2ecf20Sopenharmony_ci nphb->opal_id); 6898c2ecf20Sopenharmony_ci ret = opal_npu_destroy_context(nphb->opal_id, 0/*__unused*/, 6908c2ecf20Sopenharmony_ci pci_dev_id(gpdev)); 6918c2ecf20Sopenharmony_ci if (ret < 0) { 6928c2ecf20Sopenharmony_ci dev_err(&gpdev->dev, "Failed to destroy context: %d\n", ret); 6938c2ecf20Sopenharmony_ci return ret; 6948c2ecf20Sopenharmony_ci } 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci /* Set LPID to 0 anyway, just to be safe */ 6978c2ecf20Sopenharmony_ci dev_dbg(&gpdev->dev, "Map LPAR opalid=%llu lparid=0\n", nphb->opal_id); 6988c2ecf20Sopenharmony_ci ret = opal_npu_map_lpar(nphb->opal_id, pci_dev_id(gpdev), 0 /*LPID*/, 6998c2ecf20Sopenharmony_ci 0 /* LPCR bits */); 7008c2ecf20Sopenharmony_ci if (ret) 7018c2ecf20Sopenharmony_ci dev_err(&gpdev->dev, "Error %d mapping device to LPAR\n", ret); 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci return ret; 7048c2ecf20Sopenharmony_ci} 7058c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(pnv_npu2_unmap_lpar_dev); 706