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