12025cf9eSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 288ef16d8STomasz Nowicki /* 388ef16d8STomasz Nowicki * Copyright (C) 2016, Semihalf 488ef16d8STomasz Nowicki * Author: Tomasz Nowicki <tn@semihalf.com> 588ef16d8STomasz Nowicki * 688ef16d8STomasz Nowicki * This file implements early detection/parsing of I/O mapping 788ef16d8STomasz Nowicki * reported to OS through firmware via I/O Remapping Table (IORT) 888ef16d8STomasz Nowicki * IORT document number: ARM DEN 0049A 988ef16d8STomasz Nowicki */ 1088ef16d8STomasz Nowicki 1188ef16d8STomasz Nowicki #define pr_fmt(fmt) "ACPI: IORT: " fmt 1288ef16d8STomasz Nowicki 1388ef16d8STomasz Nowicki #include <linux/acpi_iort.h> 14da22565dSJean-Philippe Brucker #include <linux/bitfield.h> 15846f0e9eSLorenzo Pieralisi #include <linux/iommu.h> 1688ef16d8STomasz Nowicki #include <linux/kernel.h> 177936df92SLorenzo Pieralisi #include <linux/list.h> 1888ef16d8STomasz Nowicki #include <linux/pci.h> 19846f0e9eSLorenzo Pieralisi #include <linux/platform_device.h> 207936df92SLorenzo Pieralisi #include <linux/slab.h> 2188ef16d8STomasz Nowicki 22ea50b524SLorenzo Pieralisi #define IORT_TYPE_MASK(type) (1 << (type)) 23ea50b524SLorenzo Pieralisi #define IORT_MSI_TYPE (1 << ACPI_IORT_NODE_ITS_GROUP) 24643b8e4dSLorenzo Pieralisi #define IORT_IOMMU_TYPE ((1 << ACPI_IORT_NODE_SMMU) | \ 25643b8e4dSLorenzo Pieralisi (1 << ACPI_IORT_NODE_SMMU_V3)) 26ea50b524SLorenzo Pieralisi 274bf2efd2STomasz Nowicki struct iort_its_msi_chip { 284bf2efd2STomasz Nowicki struct list_head list; 294bf2efd2STomasz Nowicki struct fwnode_handle *fw_node; 308b4282e6SShameer Kolothum phys_addr_t base_addr; 314bf2efd2STomasz Nowicki u32 translation_id; 324bf2efd2STomasz Nowicki }; 334bf2efd2STomasz Nowicki 347936df92SLorenzo Pieralisi struct iort_fwnode { 357936df92SLorenzo Pieralisi struct list_head list; 367936df92SLorenzo Pieralisi struct acpi_iort_node *iort_node; 377936df92SLorenzo Pieralisi struct fwnode_handle *fwnode; 387936df92SLorenzo Pieralisi }; 397936df92SLorenzo Pieralisi static LIST_HEAD(iort_fwnode_list); 407936df92SLorenzo Pieralisi static DEFINE_SPINLOCK(iort_fwnode_lock); 417936df92SLorenzo Pieralisi 427936df92SLorenzo Pieralisi /** 437936df92SLorenzo Pieralisi * iort_set_fwnode() - Create iort_fwnode and use it to register 447936df92SLorenzo Pieralisi * iommu data in the iort_fwnode_list 457936df92SLorenzo Pieralisi * 467936df92SLorenzo Pieralisi * @node: IORT table node associated with the IOMMU 477936df92SLorenzo Pieralisi * @fwnode: fwnode associated with the IORT node 487936df92SLorenzo Pieralisi * 497936df92SLorenzo Pieralisi * Returns: 0 on success 507936df92SLorenzo Pieralisi * <0 on failure 517936df92SLorenzo Pieralisi */ 527936df92SLorenzo Pieralisi static inline int iort_set_fwnode(struct acpi_iort_node *iort_node, 537936df92SLorenzo Pieralisi struct fwnode_handle *fwnode) 547936df92SLorenzo Pieralisi { 557936df92SLorenzo Pieralisi struct iort_fwnode *np; 567936df92SLorenzo Pieralisi 577936df92SLorenzo Pieralisi np = kzalloc(sizeof(struct iort_fwnode), GFP_ATOMIC); 587936df92SLorenzo Pieralisi 597936df92SLorenzo Pieralisi if (WARN_ON(!np)) 607936df92SLorenzo Pieralisi return -ENOMEM; 617936df92SLorenzo Pieralisi 627936df92SLorenzo Pieralisi INIT_LIST_HEAD(&np->list); 637936df92SLorenzo Pieralisi np->iort_node = iort_node; 647936df92SLorenzo Pieralisi np->fwnode = fwnode; 657936df92SLorenzo Pieralisi 667936df92SLorenzo Pieralisi spin_lock(&iort_fwnode_lock); 677936df92SLorenzo Pieralisi list_add_tail(&np->list, &iort_fwnode_list); 687936df92SLorenzo Pieralisi spin_unlock(&iort_fwnode_lock); 697936df92SLorenzo Pieralisi 707936df92SLorenzo Pieralisi return 0; 717936df92SLorenzo Pieralisi } 727936df92SLorenzo Pieralisi 737936df92SLorenzo Pieralisi /** 747936df92SLorenzo Pieralisi * iort_get_fwnode() - Retrieve fwnode associated with an IORT node 757936df92SLorenzo Pieralisi * 767936df92SLorenzo Pieralisi * @node: IORT table node to be looked-up 777936df92SLorenzo Pieralisi * 787936df92SLorenzo Pieralisi * Returns: fwnode_handle pointer on success, NULL on failure 797936df92SLorenzo Pieralisi */ 80e3d49392SLorenzo Pieralisi static inline struct fwnode_handle *iort_get_fwnode( 81e3d49392SLorenzo Pieralisi struct acpi_iort_node *node) 827936df92SLorenzo Pieralisi { 837936df92SLorenzo Pieralisi struct iort_fwnode *curr; 847936df92SLorenzo Pieralisi struct fwnode_handle *fwnode = NULL; 857936df92SLorenzo Pieralisi 867936df92SLorenzo Pieralisi spin_lock(&iort_fwnode_lock); 877936df92SLorenzo Pieralisi list_for_each_entry(curr, &iort_fwnode_list, list) { 887936df92SLorenzo Pieralisi if (curr->iort_node == node) { 897936df92SLorenzo Pieralisi fwnode = curr->fwnode; 907936df92SLorenzo Pieralisi break; 917936df92SLorenzo Pieralisi } 927936df92SLorenzo Pieralisi } 937936df92SLorenzo Pieralisi spin_unlock(&iort_fwnode_lock); 947936df92SLorenzo Pieralisi 957936df92SLorenzo Pieralisi return fwnode; 967936df92SLorenzo Pieralisi } 977936df92SLorenzo Pieralisi 987936df92SLorenzo Pieralisi /** 997936df92SLorenzo Pieralisi * iort_delete_fwnode() - Delete fwnode associated with an IORT node 1007936df92SLorenzo Pieralisi * 1017936df92SLorenzo Pieralisi * @node: IORT table node associated with fwnode to delete 1027936df92SLorenzo Pieralisi */ 1037936df92SLorenzo Pieralisi static inline void iort_delete_fwnode(struct acpi_iort_node *node) 1047936df92SLorenzo Pieralisi { 1057936df92SLorenzo Pieralisi struct iort_fwnode *curr, *tmp; 1067936df92SLorenzo Pieralisi 1077936df92SLorenzo Pieralisi spin_lock(&iort_fwnode_lock); 1087936df92SLorenzo Pieralisi list_for_each_entry_safe(curr, tmp, &iort_fwnode_list, list) { 1097936df92SLorenzo Pieralisi if (curr->iort_node == node) { 1107936df92SLorenzo Pieralisi list_del(&curr->list); 1117936df92SLorenzo Pieralisi kfree(curr); 1127936df92SLorenzo Pieralisi break; 1137936df92SLorenzo Pieralisi } 1147936df92SLorenzo Pieralisi } 1157936df92SLorenzo Pieralisi spin_unlock(&iort_fwnode_lock); 1167936df92SLorenzo Pieralisi } 1177936df92SLorenzo Pieralisi 1180a71d8b9SHanjun Guo /** 1190a71d8b9SHanjun Guo * iort_get_iort_node() - Retrieve iort_node associated with an fwnode 1200a71d8b9SHanjun Guo * 1210a71d8b9SHanjun Guo * @fwnode: fwnode associated with device to be looked-up 1220a71d8b9SHanjun Guo * 1230a71d8b9SHanjun Guo * Returns: iort_node pointer on success, NULL on failure 1240a71d8b9SHanjun Guo */ 1250a71d8b9SHanjun Guo static inline struct acpi_iort_node *iort_get_iort_node( 1260a71d8b9SHanjun Guo struct fwnode_handle *fwnode) 1270a71d8b9SHanjun Guo { 1280a71d8b9SHanjun Guo struct iort_fwnode *curr; 1290a71d8b9SHanjun Guo struct acpi_iort_node *iort_node = NULL; 1300a71d8b9SHanjun Guo 1310a71d8b9SHanjun Guo spin_lock(&iort_fwnode_lock); 1320a71d8b9SHanjun Guo list_for_each_entry(curr, &iort_fwnode_list, list) { 1330a71d8b9SHanjun Guo if (curr->fwnode == fwnode) { 1340a71d8b9SHanjun Guo iort_node = curr->iort_node; 1350a71d8b9SHanjun Guo break; 1360a71d8b9SHanjun Guo } 1370a71d8b9SHanjun Guo } 1380a71d8b9SHanjun Guo spin_unlock(&iort_fwnode_lock); 1390a71d8b9SHanjun Guo 1400a71d8b9SHanjun Guo return iort_node; 1410a71d8b9SHanjun Guo } 1420a71d8b9SHanjun Guo 14388ef16d8STomasz Nowicki typedef acpi_status (*iort_find_node_callback) 14488ef16d8STomasz Nowicki (struct acpi_iort_node *node, void *context); 14588ef16d8STomasz Nowicki 14688ef16d8STomasz Nowicki /* Root pointer to the mapped IORT table */ 14788ef16d8STomasz Nowicki static struct acpi_table_header *iort_table; 14888ef16d8STomasz Nowicki 14988ef16d8STomasz Nowicki static LIST_HEAD(iort_msi_chip_list); 15088ef16d8STomasz Nowicki static DEFINE_SPINLOCK(iort_msi_chip_lock); 15188ef16d8STomasz Nowicki 1524bf2efd2STomasz Nowicki /** 1538b4282e6SShameer Kolothum * iort_register_domain_token() - register domain token along with related 1548b4282e6SShameer Kolothum * ITS ID and base address to the list from where we can get it back later on. 1554bf2efd2STomasz Nowicki * @trans_id: ITS ID. 1568b4282e6SShameer Kolothum * @base: ITS base address. 1574bf2efd2STomasz Nowicki * @fw_node: Domain token. 1584bf2efd2STomasz Nowicki * 1594bf2efd2STomasz Nowicki * Returns: 0 on success, -ENOMEM if no memory when allocating list element 1604bf2efd2STomasz Nowicki */ 1618b4282e6SShameer Kolothum int iort_register_domain_token(int trans_id, phys_addr_t base, 1628b4282e6SShameer Kolothum struct fwnode_handle *fw_node) 1634bf2efd2STomasz Nowicki { 1644bf2efd2STomasz Nowicki struct iort_its_msi_chip *its_msi_chip; 1654bf2efd2STomasz Nowicki 1664bf2efd2STomasz Nowicki its_msi_chip = kzalloc(sizeof(*its_msi_chip), GFP_KERNEL); 1674bf2efd2STomasz Nowicki if (!its_msi_chip) 1684bf2efd2STomasz Nowicki return -ENOMEM; 1694bf2efd2STomasz Nowicki 1704bf2efd2STomasz Nowicki its_msi_chip->fw_node = fw_node; 1714bf2efd2STomasz Nowicki its_msi_chip->translation_id = trans_id; 1728b4282e6SShameer Kolothum its_msi_chip->base_addr = base; 1734bf2efd2STomasz Nowicki 1744bf2efd2STomasz Nowicki spin_lock(&iort_msi_chip_lock); 1754bf2efd2STomasz Nowicki list_add(&its_msi_chip->list, &iort_msi_chip_list); 1764bf2efd2STomasz Nowicki spin_unlock(&iort_msi_chip_lock); 1774bf2efd2STomasz Nowicki 1784bf2efd2STomasz Nowicki return 0; 1794bf2efd2STomasz Nowicki } 1804bf2efd2STomasz Nowicki 1814bf2efd2STomasz Nowicki /** 1824bf2efd2STomasz Nowicki * iort_deregister_domain_token() - Deregister domain token based on ITS ID 1834bf2efd2STomasz Nowicki * @trans_id: ITS ID. 1844bf2efd2STomasz Nowicki * 1854bf2efd2STomasz Nowicki * Returns: none. 1864bf2efd2STomasz Nowicki */ 1874bf2efd2STomasz Nowicki void iort_deregister_domain_token(int trans_id) 1884bf2efd2STomasz Nowicki { 1894bf2efd2STomasz Nowicki struct iort_its_msi_chip *its_msi_chip, *t; 1904bf2efd2STomasz Nowicki 1914bf2efd2STomasz Nowicki spin_lock(&iort_msi_chip_lock); 1924bf2efd2STomasz Nowicki list_for_each_entry_safe(its_msi_chip, t, &iort_msi_chip_list, list) { 1934bf2efd2STomasz Nowicki if (its_msi_chip->translation_id == trans_id) { 1944bf2efd2STomasz Nowicki list_del(&its_msi_chip->list); 1954bf2efd2STomasz Nowicki kfree(its_msi_chip); 1964bf2efd2STomasz Nowicki break; 1974bf2efd2STomasz Nowicki } 1984bf2efd2STomasz Nowicki } 1994bf2efd2STomasz Nowicki spin_unlock(&iort_msi_chip_lock); 2004bf2efd2STomasz Nowicki } 2014bf2efd2STomasz Nowicki 2024bf2efd2STomasz Nowicki /** 2034bf2efd2STomasz Nowicki * iort_find_domain_token() - Find domain token based on given ITS ID 2044bf2efd2STomasz Nowicki * @trans_id: ITS ID. 2054bf2efd2STomasz Nowicki * 2064bf2efd2STomasz Nowicki * Returns: domain token when find on the list, NULL otherwise 2074bf2efd2STomasz Nowicki */ 2084bf2efd2STomasz Nowicki struct fwnode_handle *iort_find_domain_token(int trans_id) 2094bf2efd2STomasz Nowicki { 2104bf2efd2STomasz Nowicki struct fwnode_handle *fw_node = NULL; 2114bf2efd2STomasz Nowicki struct iort_its_msi_chip *its_msi_chip; 2124bf2efd2STomasz Nowicki 2134bf2efd2STomasz Nowicki spin_lock(&iort_msi_chip_lock); 2144bf2efd2STomasz Nowicki list_for_each_entry(its_msi_chip, &iort_msi_chip_list, list) { 2154bf2efd2STomasz Nowicki if (its_msi_chip->translation_id == trans_id) { 2164bf2efd2STomasz Nowicki fw_node = its_msi_chip->fw_node; 2174bf2efd2STomasz Nowicki break; 2184bf2efd2STomasz Nowicki } 2194bf2efd2STomasz Nowicki } 2204bf2efd2STomasz Nowicki spin_unlock(&iort_msi_chip_lock); 2214bf2efd2STomasz Nowicki 2224bf2efd2STomasz Nowicki return fw_node; 2234bf2efd2STomasz Nowicki } 2244bf2efd2STomasz Nowicki 22588ef16d8STomasz Nowicki static struct acpi_iort_node *iort_scan_node(enum acpi_iort_node_type type, 22688ef16d8STomasz Nowicki iort_find_node_callback callback, 22788ef16d8STomasz Nowicki void *context) 22888ef16d8STomasz Nowicki { 22988ef16d8STomasz Nowicki struct acpi_iort_node *iort_node, *iort_end; 23088ef16d8STomasz Nowicki struct acpi_table_iort *iort; 23188ef16d8STomasz Nowicki int i; 23288ef16d8STomasz Nowicki 23388ef16d8STomasz Nowicki if (!iort_table) 23488ef16d8STomasz Nowicki return NULL; 23588ef16d8STomasz Nowicki 23688ef16d8STomasz Nowicki /* Get the first IORT node */ 23788ef16d8STomasz Nowicki iort = (struct acpi_table_iort *)iort_table; 23888ef16d8STomasz Nowicki iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort, 23988ef16d8STomasz Nowicki iort->node_offset); 24088ef16d8STomasz Nowicki iort_end = ACPI_ADD_PTR(struct acpi_iort_node, iort_table, 24188ef16d8STomasz Nowicki iort_table->length); 24288ef16d8STomasz Nowicki 24388ef16d8STomasz Nowicki for (i = 0; i < iort->node_count; i++) { 24488ef16d8STomasz Nowicki if (WARN_TAINT(iort_node >= iort_end, TAINT_FIRMWARE_WORKAROUND, 24588ef16d8STomasz Nowicki "IORT node pointer overflows, bad table!\n")) 24688ef16d8STomasz Nowicki return NULL; 24788ef16d8STomasz Nowicki 24888ef16d8STomasz Nowicki if (iort_node->type == type && 24988ef16d8STomasz Nowicki ACPI_SUCCESS(callback(iort_node, context))) 25088ef16d8STomasz Nowicki return iort_node; 25188ef16d8STomasz Nowicki 25288ef16d8STomasz Nowicki iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort_node, 25388ef16d8STomasz Nowicki iort_node->length); 25488ef16d8STomasz Nowicki } 25588ef16d8STomasz Nowicki 25688ef16d8STomasz Nowicki return NULL; 25788ef16d8STomasz Nowicki } 25888ef16d8STomasz Nowicki 25988ef16d8STomasz Nowicki static acpi_status iort_match_node_callback(struct acpi_iort_node *node, 26088ef16d8STomasz Nowicki void *context) 26188ef16d8STomasz Nowicki { 26288ef16d8STomasz Nowicki struct device *dev = context; 263c92bdfe8SHanjun Guo acpi_status status = AE_NOT_FOUND; 26488ef16d8STomasz Nowicki 26588ef16d8STomasz Nowicki if (node->type == ACPI_IORT_NODE_NAMED_COMPONENT) { 26688ef16d8STomasz Nowicki struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL }; 26707d2e59fSLorenzo Pieralisi struct acpi_device *adev; 26888ef16d8STomasz Nowicki struct acpi_iort_named_component *ncomp; 26907d2e59fSLorenzo Pieralisi struct device *nc_dev = dev; 27007d2e59fSLorenzo Pieralisi 27107d2e59fSLorenzo Pieralisi /* 27207d2e59fSLorenzo Pieralisi * Walk the device tree to find a device with an 27307d2e59fSLorenzo Pieralisi * ACPI companion; there is no point in scanning 27407d2e59fSLorenzo Pieralisi * IORT for a device matching a named component if 27507d2e59fSLorenzo Pieralisi * the device does not have an ACPI companion to 27607d2e59fSLorenzo Pieralisi * start with. 27707d2e59fSLorenzo Pieralisi */ 27807d2e59fSLorenzo Pieralisi do { 27907d2e59fSLorenzo Pieralisi adev = ACPI_COMPANION(nc_dev); 28007d2e59fSLorenzo Pieralisi if (adev) 28107d2e59fSLorenzo Pieralisi break; 28207d2e59fSLorenzo Pieralisi 28307d2e59fSLorenzo Pieralisi nc_dev = nc_dev->parent; 28407d2e59fSLorenzo Pieralisi } while (nc_dev); 28588ef16d8STomasz Nowicki 286c92bdfe8SHanjun Guo if (!adev) 28788ef16d8STomasz Nowicki goto out; 28888ef16d8STomasz Nowicki 28988ef16d8STomasz Nowicki status = acpi_get_name(adev->handle, ACPI_FULL_PATHNAME, &buf); 29088ef16d8STomasz Nowicki if (ACPI_FAILURE(status)) { 29107d2e59fSLorenzo Pieralisi dev_warn(nc_dev, "Can't get device full path name\n"); 29288ef16d8STomasz Nowicki goto out; 29388ef16d8STomasz Nowicki } 29488ef16d8STomasz Nowicki 29588ef16d8STomasz Nowicki ncomp = (struct acpi_iort_named_component *)node->node_data; 29688ef16d8STomasz Nowicki status = !strcmp(ncomp->device_name, buf.pointer) ? 29788ef16d8STomasz Nowicki AE_OK : AE_NOT_FOUND; 29888ef16d8STomasz Nowicki acpi_os_free(buf.pointer); 29988ef16d8STomasz Nowicki } else if (node->type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX) { 30088ef16d8STomasz Nowicki struct acpi_iort_root_complex *pci_rc; 30188ef16d8STomasz Nowicki struct pci_bus *bus; 30288ef16d8STomasz Nowicki 30388ef16d8STomasz Nowicki bus = to_pci_bus(dev); 30488ef16d8STomasz Nowicki pci_rc = (struct acpi_iort_root_complex *)node->node_data; 30588ef16d8STomasz Nowicki 30688ef16d8STomasz Nowicki /* 30788ef16d8STomasz Nowicki * It is assumed that PCI segment numbers maps one-to-one 30888ef16d8STomasz Nowicki * with root complexes. Each segment number can represent only 30988ef16d8STomasz Nowicki * one root complex. 31088ef16d8STomasz Nowicki */ 31188ef16d8STomasz Nowicki status = pci_rc->pci_segment_number == pci_domain_nr(bus) ? 31288ef16d8STomasz Nowicki AE_OK : AE_NOT_FOUND; 31388ef16d8STomasz Nowicki } 31488ef16d8STomasz Nowicki out: 31588ef16d8STomasz Nowicki return status; 31688ef16d8STomasz Nowicki } 31788ef16d8STomasz Nowicki 31888ef16d8STomasz Nowicki static int iort_id_map(struct acpi_iort_id_mapping *map, u8 type, u32 rid_in, 319539979b6SArd Biesheuvel u32 *rid_out, bool check_overlap) 32088ef16d8STomasz Nowicki { 32188ef16d8STomasz Nowicki /* Single mapping does not care for input id */ 32288ef16d8STomasz Nowicki if (map->flags & ACPI_IORT_ID_SINGLE_MAPPING) { 32388ef16d8STomasz Nowicki if (type == ACPI_IORT_NODE_NAMED_COMPONENT || 32488ef16d8STomasz Nowicki type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX) { 32588ef16d8STomasz Nowicki *rid_out = map->output_base; 32688ef16d8STomasz Nowicki return 0; 32788ef16d8STomasz Nowicki } 32888ef16d8STomasz Nowicki 32988ef16d8STomasz Nowicki pr_warn(FW_BUG "[map %p] SINGLE MAPPING flag not allowed for node type %d, skipping ID map\n", 33088ef16d8STomasz Nowicki map, type); 33188ef16d8STomasz Nowicki return -ENXIO; 33288ef16d8STomasz Nowicki } 33388ef16d8STomasz Nowicki 3346d3b29d0SArd Biesheuvel if (rid_in < map->input_base || 335539979b6SArd Biesheuvel (rid_in > map->input_base + map->id_count)) 33688ef16d8STomasz Nowicki return -ENXIO; 33788ef16d8STomasz Nowicki 338539979b6SArd Biesheuvel if (check_overlap) { 339539979b6SArd Biesheuvel /* 340539979b6SArd Biesheuvel * We already found a mapping for this input ID at the end of 341539979b6SArd Biesheuvel * another region. If it coincides with the start of this 342539979b6SArd Biesheuvel * region, we assume the prior match was due to the off-by-1 343539979b6SArd Biesheuvel * issue mentioned below, and allow it to be superseded. 344539979b6SArd Biesheuvel * Otherwise, things are *really* broken, and we just disregard 345539979b6SArd Biesheuvel * duplicate matches entirely to retain compatibility. 346539979b6SArd Biesheuvel */ 347539979b6SArd Biesheuvel pr_err(FW_BUG "[map %p] conflicting mapping for input ID 0x%x\n", 348539979b6SArd Biesheuvel map, rid_in); 349539979b6SArd Biesheuvel if (rid_in != map->input_base) 350539979b6SArd Biesheuvel return -ENXIO; 35144cdc7b1SHanjun Guo 35244cdc7b1SHanjun Guo pr_err(FW_BUG "applying workaround.\n"); 353539979b6SArd Biesheuvel } 354539979b6SArd Biesheuvel 35588ef16d8STomasz Nowicki *rid_out = map->output_base + (rid_in - map->input_base); 356539979b6SArd Biesheuvel 357539979b6SArd Biesheuvel /* 358539979b6SArd Biesheuvel * Due to confusion regarding the meaning of the id_count field (which 359539979b6SArd Biesheuvel * carries the number of IDs *minus 1*), we may have to disregard this 360539979b6SArd Biesheuvel * match if it is at the end of the range, and overlaps with the start 361539979b6SArd Biesheuvel * of another one. 362539979b6SArd Biesheuvel */ 363539979b6SArd Biesheuvel if (map->id_count > 0 && rid_in == map->input_base + map->id_count) 364539979b6SArd Biesheuvel return -EAGAIN; 36588ef16d8STomasz Nowicki return 0; 36688ef16d8STomasz Nowicki } 36788ef16d8STomasz Nowicki 368e3d49392SLorenzo Pieralisi static struct acpi_iort_node *iort_node_get_id(struct acpi_iort_node *node, 3698ca4f1d3SHanjun Guo u32 *id_out, int index) 370618f535aSLorenzo Pieralisi { 371618f535aSLorenzo Pieralisi struct acpi_iort_node *parent; 372618f535aSLorenzo Pieralisi struct acpi_iort_id_mapping *map; 373618f535aSLorenzo Pieralisi 374618f535aSLorenzo Pieralisi if (!node->mapping_offset || !node->mapping_count || 375618f535aSLorenzo Pieralisi index >= node->mapping_count) 376618f535aSLorenzo Pieralisi return NULL; 377618f535aSLorenzo Pieralisi 378618f535aSLorenzo Pieralisi map = ACPI_ADD_PTR(struct acpi_iort_id_mapping, node, 379030abd8aSLorenzo Pieralisi node->mapping_offset + index * sizeof(*map)); 380618f535aSLorenzo Pieralisi 381618f535aSLorenzo Pieralisi /* Firmware bug! */ 382618f535aSLorenzo Pieralisi if (!map->output_reference) { 383618f535aSLorenzo Pieralisi pr_err(FW_BUG "[node %p type %d] ID map has NULL parent reference\n", 384618f535aSLorenzo Pieralisi node, node->type); 385618f535aSLorenzo Pieralisi return NULL; 386618f535aSLorenzo Pieralisi } 387618f535aSLorenzo Pieralisi 388618f535aSLorenzo Pieralisi parent = ACPI_ADD_PTR(struct acpi_iort_node, iort_table, 389618f535aSLorenzo Pieralisi map->output_reference); 390618f535aSLorenzo Pieralisi 391030abd8aSLorenzo Pieralisi if (map->flags & ACPI_IORT_ID_SINGLE_MAPPING) { 392618f535aSLorenzo Pieralisi if (node->type == ACPI_IORT_NODE_NAMED_COMPONENT || 39386456a3fSHanjun Guo node->type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX || 39424e51604SNeil Leeder node->type == ACPI_IORT_NODE_SMMU_V3 || 39524e51604SNeil Leeder node->type == ACPI_IORT_NODE_PMCG) { 396030abd8aSLorenzo Pieralisi *id_out = map->output_base; 397618f535aSLorenzo Pieralisi return parent; 398618f535aSLorenzo Pieralisi } 399618f535aSLorenzo Pieralisi } 400618f535aSLorenzo Pieralisi 401618f535aSLorenzo Pieralisi return NULL; 402618f535aSLorenzo Pieralisi } 403618f535aSLorenzo Pieralisi 40486456a3fSHanjun Guo static int iort_get_id_mapping_index(struct acpi_iort_node *node) 40586456a3fSHanjun Guo { 40686456a3fSHanjun Guo struct acpi_iort_smmu_v3 *smmu; 40750c8ab8dSTuan Phan struct acpi_iort_pmcg *pmcg; 40886456a3fSHanjun Guo 40986456a3fSHanjun Guo switch (node->type) { 41086456a3fSHanjun Guo case ACPI_IORT_NODE_SMMU_V3: 41186456a3fSHanjun Guo /* 41286456a3fSHanjun Guo * SMMUv3 dev ID mapping index was introduced in revision 1 41386456a3fSHanjun Guo * table, not available in revision 0 41486456a3fSHanjun Guo */ 41586456a3fSHanjun Guo if (node->revision < 1) 41686456a3fSHanjun Guo return -EINVAL; 41786456a3fSHanjun Guo 41886456a3fSHanjun Guo smmu = (struct acpi_iort_smmu_v3 *)node->node_data; 41986456a3fSHanjun Guo /* 42086456a3fSHanjun Guo * ID mapping index is only ignored if all interrupts are 42186456a3fSHanjun Guo * GSIV based 42286456a3fSHanjun Guo */ 42386456a3fSHanjun Guo if (smmu->event_gsiv && smmu->pri_gsiv && smmu->gerr_gsiv 42486456a3fSHanjun Guo && smmu->sync_gsiv) 42586456a3fSHanjun Guo return -EINVAL; 42686456a3fSHanjun Guo 42786456a3fSHanjun Guo if (smmu->id_mapping_index >= node->mapping_count) { 42886456a3fSHanjun Guo pr_err(FW_BUG "[node %p type %d] ID mapping index overflows valid mappings\n", 42986456a3fSHanjun Guo node, node->type); 43086456a3fSHanjun Guo return -EINVAL; 43186456a3fSHanjun Guo } 43286456a3fSHanjun Guo 43386456a3fSHanjun Guo return smmu->id_mapping_index; 43424e51604SNeil Leeder case ACPI_IORT_NODE_PMCG: 43550c8ab8dSTuan Phan pmcg = (struct acpi_iort_pmcg *)node->node_data; 43650c8ab8dSTuan Phan if (pmcg->overflow_gsiv || node->mapping_count == 0) 43750c8ab8dSTuan Phan return -EINVAL; 43850c8ab8dSTuan Phan 43924e51604SNeil Leeder return 0; 44086456a3fSHanjun Guo default: 44186456a3fSHanjun Guo return -EINVAL; 44286456a3fSHanjun Guo } 44386456a3fSHanjun Guo } 4448c8df8dcSHanjun Guo 445697f6093SHanjun Guo static struct acpi_iort_node *iort_node_map_id(struct acpi_iort_node *node, 446697f6093SHanjun Guo u32 id_in, u32 *id_out, 447ea50b524SLorenzo Pieralisi u8 type_mask) 44888ef16d8STomasz Nowicki { 449697f6093SHanjun Guo u32 id = id_in; 45088ef16d8STomasz Nowicki 45188ef16d8STomasz Nowicki /* Parse the ID mapping tree to find specified node type */ 45288ef16d8STomasz Nowicki while (node) { 45388ef16d8STomasz Nowicki struct acpi_iort_id_mapping *map; 454539979b6SArd Biesheuvel int i, index, rc = 0; 455539979b6SArd Biesheuvel u32 out_ref = 0, map_id = id; 45688ef16d8STomasz Nowicki 457ea50b524SLorenzo Pieralisi if (IORT_TYPE_MASK(node->type) & type_mask) { 458697f6093SHanjun Guo if (id_out) 459697f6093SHanjun Guo *id_out = id; 46088ef16d8STomasz Nowicki return node; 46188ef16d8STomasz Nowicki } 46288ef16d8STomasz Nowicki 46388ef16d8STomasz Nowicki if (!node->mapping_offset || !node->mapping_count) 46488ef16d8STomasz Nowicki goto fail_map; 46588ef16d8STomasz Nowicki 46688ef16d8STomasz Nowicki map = ACPI_ADD_PTR(struct acpi_iort_id_mapping, node, 46788ef16d8STomasz Nowicki node->mapping_offset); 46888ef16d8STomasz Nowicki 46988ef16d8STomasz Nowicki /* Firmware bug! */ 47088ef16d8STomasz Nowicki if (!map->output_reference) { 47188ef16d8STomasz Nowicki pr_err(FW_BUG "[node %p type %d] ID map has NULL parent reference\n", 47288ef16d8STomasz Nowicki node, node->type); 47388ef16d8STomasz Nowicki goto fail_map; 47488ef16d8STomasz Nowicki } 47588ef16d8STomasz Nowicki 4768c8df8dcSHanjun Guo /* 4778c8df8dcSHanjun Guo * Get the special ID mapping index (if any) and skip its 4788c8df8dcSHanjun Guo * associated ID map to prevent erroneous multi-stage 4798c8df8dcSHanjun Guo * IORT ID translations. 4808c8df8dcSHanjun Guo */ 4818c8df8dcSHanjun Guo index = iort_get_id_mapping_index(node); 4828c8df8dcSHanjun Guo 483697f6093SHanjun Guo /* Do the ID translation */ 48488ef16d8STomasz Nowicki for (i = 0; i < node->mapping_count; i++, map++) { 4858c8df8dcSHanjun Guo /* if it is special mapping index, skip it */ 4868c8df8dcSHanjun Guo if (i == index) 4878c8df8dcSHanjun Guo continue; 4888c8df8dcSHanjun Guo 489539979b6SArd Biesheuvel rc = iort_id_map(map, node->type, map_id, &id, out_ref); 490539979b6SArd Biesheuvel if (!rc) 49188ef16d8STomasz Nowicki break; 492539979b6SArd Biesheuvel if (rc == -EAGAIN) 493539979b6SArd Biesheuvel out_ref = map->output_reference; 49488ef16d8STomasz Nowicki } 49588ef16d8STomasz Nowicki 496539979b6SArd Biesheuvel if (i == node->mapping_count && !out_ref) 49788ef16d8STomasz Nowicki goto fail_map; 49888ef16d8STomasz Nowicki 49988ef16d8STomasz Nowicki node = ACPI_ADD_PTR(struct acpi_iort_node, iort_table, 500539979b6SArd Biesheuvel rc ? out_ref : map->output_reference); 50188ef16d8STomasz Nowicki } 50288ef16d8STomasz Nowicki 50388ef16d8STomasz Nowicki fail_map: 504697f6093SHanjun Guo /* Map input ID to output ID unchanged on mapping failure */ 505697f6093SHanjun Guo if (id_out) 506697f6093SHanjun Guo *id_out = id_in; 50788ef16d8STomasz Nowicki 50888ef16d8STomasz Nowicki return NULL; 50988ef16d8STomasz Nowicki } 51088ef16d8STomasz Nowicki 511e3d49392SLorenzo Pieralisi static struct acpi_iort_node *iort_node_map_platform_id( 512e3d49392SLorenzo Pieralisi struct acpi_iort_node *node, u32 *id_out, u8 type_mask, 5138ca4f1d3SHanjun Guo int index) 5148ca4f1d3SHanjun Guo { 5158ca4f1d3SHanjun Guo struct acpi_iort_node *parent; 5168ca4f1d3SHanjun Guo u32 id; 5178ca4f1d3SHanjun Guo 5188ca4f1d3SHanjun Guo /* step 1: retrieve the initial dev id */ 5198ca4f1d3SHanjun Guo parent = iort_node_get_id(node, &id, index); 5208ca4f1d3SHanjun Guo if (!parent) 5218ca4f1d3SHanjun Guo return NULL; 5228ca4f1d3SHanjun Guo 5238ca4f1d3SHanjun Guo /* 5248ca4f1d3SHanjun Guo * optional step 2: map the initial dev id if its parent is not 5258ca4f1d3SHanjun Guo * the target type we want, map it again for the use cases such 5268ca4f1d3SHanjun Guo * as NC (named component) -> SMMU -> ITS. If the type is matched, 5278ca4f1d3SHanjun Guo * return the initial dev id and its parent pointer directly. 5288ca4f1d3SHanjun Guo */ 5298ca4f1d3SHanjun Guo if (!(IORT_TYPE_MASK(parent->type) & type_mask)) 5308ca4f1d3SHanjun Guo parent = iort_node_map_id(parent, id, id_out, type_mask); 5318ca4f1d3SHanjun Guo else 5328ca4f1d3SHanjun Guo if (id_out) 5338ca4f1d3SHanjun Guo *id_out = id; 5348ca4f1d3SHanjun Guo 5358ca4f1d3SHanjun Guo return parent; 5368ca4f1d3SHanjun Guo } 5378ca4f1d3SHanjun Guo 53888ef16d8STomasz Nowicki static struct acpi_iort_node *iort_find_dev_node(struct device *dev) 53988ef16d8STomasz Nowicki { 54088ef16d8STomasz Nowicki struct pci_bus *pbus; 54188ef16d8STomasz Nowicki 5420a71d8b9SHanjun Guo if (!dev_is_pci(dev)) { 5430a71d8b9SHanjun Guo struct acpi_iort_node *node; 5440a71d8b9SHanjun Guo /* 5450a71d8b9SHanjun Guo * scan iort_fwnode_list to see if it's an iort platform 5460a71d8b9SHanjun Guo * device (such as SMMU, PMCG),its iort node already cached 5470a71d8b9SHanjun Guo * and associated with fwnode when iort platform devices 5480a71d8b9SHanjun Guo * were initialized. 5490a71d8b9SHanjun Guo */ 5500a71d8b9SHanjun Guo node = iort_get_iort_node(dev->fwnode); 5510a71d8b9SHanjun Guo if (node) 5520a71d8b9SHanjun Guo return node; 5530a71d8b9SHanjun Guo /* 5540a71d8b9SHanjun Guo * if not, then it should be a platform device defined in 5550a71d8b9SHanjun Guo * DSDT/SSDT (with Named Component node in IORT) 5560a71d8b9SHanjun Guo */ 55788ef16d8STomasz Nowicki return iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT, 55888ef16d8STomasz Nowicki iort_match_node_callback, dev); 5590a71d8b9SHanjun Guo } 56088ef16d8STomasz Nowicki 56188ef16d8STomasz Nowicki pbus = to_pci_dev(dev)->bus; 56288ef16d8STomasz Nowicki 56388ef16d8STomasz Nowicki return iort_scan_node(ACPI_IORT_NODE_PCI_ROOT_COMPLEX, 56488ef16d8STomasz Nowicki iort_match_node_callback, &pbus->dev); 56588ef16d8STomasz Nowicki } 56688ef16d8STomasz Nowicki 5674bf2efd2STomasz Nowicki /** 56839c3cf56SLorenzo Pieralisi * iort_msi_map_id() - Map a MSI input ID for a device 5694bf2efd2STomasz Nowicki * @dev: The device for which the mapping is to be done. 57039c3cf56SLorenzo Pieralisi * @input_id: The device input ID. 5714bf2efd2STomasz Nowicki * 57239c3cf56SLorenzo Pieralisi * Returns: mapped MSI ID on success, input ID otherwise 5734bf2efd2STomasz Nowicki */ 57439c3cf56SLorenzo Pieralisi u32 iort_msi_map_id(struct device *dev, u32 input_id) 5754bf2efd2STomasz Nowicki { 5764bf2efd2STomasz Nowicki struct acpi_iort_node *node; 5774bf2efd2STomasz Nowicki u32 dev_id; 5784bf2efd2STomasz Nowicki 5794bf2efd2STomasz Nowicki node = iort_find_dev_node(dev); 5804bf2efd2STomasz Nowicki if (!node) 58139c3cf56SLorenzo Pieralisi return input_id; 5824bf2efd2STomasz Nowicki 58339c3cf56SLorenzo Pieralisi iort_node_map_id(node, input_id, &dev_id, IORT_MSI_TYPE); 5844bf2efd2STomasz Nowicki return dev_id; 5854bf2efd2STomasz Nowicki } 5864bf2efd2STomasz Nowicki 5874bf2efd2STomasz Nowicki /** 588ae7c1838SHanjun Guo * iort_pmsi_get_dev_id() - Get the device id for a device 589ae7c1838SHanjun Guo * @dev: The device for which the mapping is to be done. 590ae7c1838SHanjun Guo * @dev_id: The device ID found. 591ae7c1838SHanjun Guo * 592ae7c1838SHanjun Guo * Returns: 0 for successful find a dev id, -ENODEV on error 593ae7c1838SHanjun Guo */ 594ae7c1838SHanjun Guo int iort_pmsi_get_dev_id(struct device *dev, u32 *dev_id) 595ae7c1838SHanjun Guo { 5968c8df8dcSHanjun Guo int i, index; 597ae7c1838SHanjun Guo struct acpi_iort_node *node; 598ae7c1838SHanjun Guo 599ae7c1838SHanjun Guo node = iort_find_dev_node(dev); 600ae7c1838SHanjun Guo if (!node) 601ae7c1838SHanjun Guo return -ENODEV; 602ae7c1838SHanjun Guo 6038c8df8dcSHanjun Guo index = iort_get_id_mapping_index(node); 6048c8df8dcSHanjun Guo /* if there is a valid index, go get the dev_id directly */ 6058c8df8dcSHanjun Guo if (index >= 0) { 6068c8df8dcSHanjun Guo if (iort_node_get_id(node, dev_id, index)) 607ae7c1838SHanjun Guo return 0; 6088c8df8dcSHanjun Guo } else { 6098c8df8dcSHanjun Guo for (i = 0; i < node->mapping_count; i++) { 6108c8df8dcSHanjun Guo if (iort_node_map_platform_id(node, dev_id, 6118c8df8dcSHanjun Guo IORT_MSI_TYPE, i)) 6128c8df8dcSHanjun Guo return 0; 6138c8df8dcSHanjun Guo } 614ae7c1838SHanjun Guo } 615ae7c1838SHanjun Guo 616ae7c1838SHanjun Guo return -ENODEV; 617ae7c1838SHanjun Guo } 618ae7c1838SHanjun Guo 6198b4282e6SShameer Kolothum static int __maybe_unused iort_find_its_base(u32 its_id, phys_addr_t *base) 6208b4282e6SShameer Kolothum { 6218b4282e6SShameer Kolothum struct iort_its_msi_chip *its_msi_chip; 6228b4282e6SShameer Kolothum int ret = -ENODEV; 6238b4282e6SShameer Kolothum 6248b4282e6SShameer Kolothum spin_lock(&iort_msi_chip_lock); 6258b4282e6SShameer Kolothum list_for_each_entry(its_msi_chip, &iort_msi_chip_list, list) { 6268b4282e6SShameer Kolothum if (its_msi_chip->translation_id == its_id) { 6278b4282e6SShameer Kolothum *base = its_msi_chip->base_addr; 6288b4282e6SShameer Kolothum ret = 0; 6298b4282e6SShameer Kolothum break; 6308b4282e6SShameer Kolothum } 6318b4282e6SShameer Kolothum } 6328b4282e6SShameer Kolothum spin_unlock(&iort_msi_chip_lock); 6338b4282e6SShameer Kolothum 6348b4282e6SShameer Kolothum return ret; 6358b4282e6SShameer Kolothum } 6368b4282e6SShameer Kolothum 637ae7c1838SHanjun Guo /** 6384bf2efd2STomasz Nowicki * iort_dev_find_its_id() - Find the ITS identifier for a device 6394bf2efd2STomasz Nowicki * @dev: The device. 640d1718a1bSLorenzo Pieralisi * @id: Device's ID 6414bf2efd2STomasz Nowicki * @idx: Index of the ITS identifier list. 6424bf2efd2STomasz Nowicki * @its_id: ITS identifier. 6434bf2efd2STomasz Nowicki * 6444bf2efd2STomasz Nowicki * Returns: 0 on success, appropriate error value otherwise 6454bf2efd2STomasz Nowicki */ 646d1718a1bSLorenzo Pieralisi static int iort_dev_find_its_id(struct device *dev, u32 id, 6474bf2efd2STomasz Nowicki unsigned int idx, int *its_id) 6484bf2efd2STomasz Nowicki { 6494bf2efd2STomasz Nowicki struct acpi_iort_its_group *its; 6504bf2efd2STomasz Nowicki struct acpi_iort_node *node; 6514bf2efd2STomasz Nowicki 6524bf2efd2STomasz Nowicki node = iort_find_dev_node(dev); 6534bf2efd2STomasz Nowicki if (!node) 6544bf2efd2STomasz Nowicki return -ENXIO; 6554bf2efd2STomasz Nowicki 656d1718a1bSLorenzo Pieralisi node = iort_node_map_id(node, id, NULL, IORT_MSI_TYPE); 6574bf2efd2STomasz Nowicki if (!node) 6584bf2efd2STomasz Nowicki return -ENXIO; 6594bf2efd2STomasz Nowicki 6604bf2efd2STomasz Nowicki /* Move to ITS specific data */ 6614bf2efd2STomasz Nowicki its = (struct acpi_iort_its_group *)node->node_data; 6625a46d3f7SLorenzo Pieralisi if (idx >= its->its_count) { 6635a46d3f7SLorenzo Pieralisi dev_err(dev, "requested ITS ID index [%d] overruns ITS entries [%d]\n", 6644bf2efd2STomasz Nowicki idx, its->its_count); 6654bf2efd2STomasz Nowicki return -ENXIO; 6664bf2efd2STomasz Nowicki } 6674bf2efd2STomasz Nowicki 6684bf2efd2STomasz Nowicki *its_id = its->identifiers[idx]; 6694bf2efd2STomasz Nowicki return 0; 6704bf2efd2STomasz Nowicki } 6714bf2efd2STomasz Nowicki 6724bf2efd2STomasz Nowicki /** 6734bf2efd2STomasz Nowicki * iort_get_device_domain() - Find MSI domain related to a device 6744bf2efd2STomasz Nowicki * @dev: The device. 6754bf2efd2STomasz Nowicki * @req_id: Requester ID for the device. 6764bf2efd2STomasz Nowicki * 6774bf2efd2STomasz Nowicki * Returns: the MSI domain for this device, NULL otherwise 6784bf2efd2STomasz Nowicki */ 679d1718a1bSLorenzo Pieralisi struct irq_domain *iort_get_device_domain(struct device *dev, u32 id, 680d1718a1bSLorenzo Pieralisi enum irq_domain_bus_token bus_token) 6814bf2efd2STomasz Nowicki { 6824bf2efd2STomasz Nowicki struct fwnode_handle *handle; 6834bf2efd2STomasz Nowicki int its_id; 6844bf2efd2STomasz Nowicki 685d1718a1bSLorenzo Pieralisi if (iort_dev_find_its_id(dev, id, 0, &its_id)) 6864bf2efd2STomasz Nowicki return NULL; 6874bf2efd2STomasz Nowicki 6884bf2efd2STomasz Nowicki handle = iort_find_domain_token(its_id); 6894bf2efd2STomasz Nowicki if (!handle) 6904bf2efd2STomasz Nowicki return NULL; 6914bf2efd2STomasz Nowicki 692d1718a1bSLorenzo Pieralisi return irq_find_matching_fwnode(handle, bus_token); 6934bf2efd2STomasz Nowicki } 6944bf2efd2STomasz Nowicki 69565637901SLorenzo Pieralisi static void iort_set_device_domain(struct device *dev, 69665637901SLorenzo Pieralisi struct acpi_iort_node *node) 69765637901SLorenzo Pieralisi { 69865637901SLorenzo Pieralisi struct acpi_iort_its_group *its; 69965637901SLorenzo Pieralisi struct acpi_iort_node *msi_parent; 70065637901SLorenzo Pieralisi struct acpi_iort_id_mapping *map; 70165637901SLorenzo Pieralisi struct fwnode_handle *iort_fwnode; 70265637901SLorenzo Pieralisi struct irq_domain *domain; 70365637901SLorenzo Pieralisi int index; 70465637901SLorenzo Pieralisi 70565637901SLorenzo Pieralisi index = iort_get_id_mapping_index(node); 70665637901SLorenzo Pieralisi if (index < 0) 70765637901SLorenzo Pieralisi return; 70865637901SLorenzo Pieralisi 70965637901SLorenzo Pieralisi map = ACPI_ADD_PTR(struct acpi_iort_id_mapping, node, 71065637901SLorenzo Pieralisi node->mapping_offset + index * sizeof(*map)); 71165637901SLorenzo Pieralisi 71265637901SLorenzo Pieralisi /* Firmware bug! */ 71365637901SLorenzo Pieralisi if (!map->output_reference || 71465637901SLorenzo Pieralisi !(map->flags & ACPI_IORT_ID_SINGLE_MAPPING)) { 71565637901SLorenzo Pieralisi pr_err(FW_BUG "[node %p type %d] Invalid MSI mapping\n", 71665637901SLorenzo Pieralisi node, node->type); 71765637901SLorenzo Pieralisi return; 71865637901SLorenzo Pieralisi } 71965637901SLorenzo Pieralisi 72065637901SLorenzo Pieralisi msi_parent = ACPI_ADD_PTR(struct acpi_iort_node, iort_table, 72165637901SLorenzo Pieralisi map->output_reference); 72265637901SLorenzo Pieralisi 72365637901SLorenzo Pieralisi if (!msi_parent || msi_parent->type != ACPI_IORT_NODE_ITS_GROUP) 72465637901SLorenzo Pieralisi return; 72565637901SLorenzo Pieralisi 72665637901SLorenzo Pieralisi /* Move to ITS specific data */ 72765637901SLorenzo Pieralisi its = (struct acpi_iort_its_group *)msi_parent->node_data; 72865637901SLorenzo Pieralisi 72965637901SLorenzo Pieralisi iort_fwnode = iort_find_domain_token(its->identifiers[0]); 73065637901SLorenzo Pieralisi if (!iort_fwnode) 73165637901SLorenzo Pieralisi return; 73265637901SLorenzo Pieralisi 73365637901SLorenzo Pieralisi domain = irq_find_matching_fwnode(iort_fwnode, DOMAIN_BUS_PLATFORM_MSI); 73465637901SLorenzo Pieralisi if (domain) 73565637901SLorenzo Pieralisi dev_set_msi_domain(dev, domain); 73665637901SLorenzo Pieralisi } 73765637901SLorenzo Pieralisi 738d4f54a18SHanjun Guo /** 739d4f54a18SHanjun Guo * iort_get_platform_device_domain() - Find MSI domain related to a 740d4f54a18SHanjun Guo * platform device 741d4f54a18SHanjun Guo * @dev: the dev pointer associated with the platform device 742d4f54a18SHanjun Guo * 743d4f54a18SHanjun Guo * Returns: the MSI domain for this device, NULL otherwise 744d4f54a18SHanjun Guo */ 745d4f54a18SHanjun Guo static struct irq_domain *iort_get_platform_device_domain(struct device *dev) 746d4f54a18SHanjun Guo { 747ea2412dcSLorenzo Pieralisi struct acpi_iort_node *node, *msi_parent = NULL; 748d4f54a18SHanjun Guo struct fwnode_handle *iort_fwnode; 749d4f54a18SHanjun Guo struct acpi_iort_its_group *its; 750d4f54a18SHanjun Guo int i; 751d4f54a18SHanjun Guo 752d4f54a18SHanjun Guo /* find its associated iort node */ 753d4f54a18SHanjun Guo node = iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT, 754d4f54a18SHanjun Guo iort_match_node_callback, dev); 755d4f54a18SHanjun Guo if (!node) 756d4f54a18SHanjun Guo return NULL; 757d4f54a18SHanjun Guo 758d4f54a18SHanjun Guo /* then find its msi parent node */ 759d4f54a18SHanjun Guo for (i = 0; i < node->mapping_count; i++) { 760d4f54a18SHanjun Guo msi_parent = iort_node_map_platform_id(node, NULL, 761d4f54a18SHanjun Guo IORT_MSI_TYPE, i); 762d4f54a18SHanjun Guo if (msi_parent) 763d4f54a18SHanjun Guo break; 764d4f54a18SHanjun Guo } 765d4f54a18SHanjun Guo 766d4f54a18SHanjun Guo if (!msi_parent) 767d4f54a18SHanjun Guo return NULL; 768d4f54a18SHanjun Guo 769d4f54a18SHanjun Guo /* Move to ITS specific data */ 770d4f54a18SHanjun Guo its = (struct acpi_iort_its_group *)msi_parent->node_data; 771d4f54a18SHanjun Guo 772d4f54a18SHanjun Guo iort_fwnode = iort_find_domain_token(its->identifiers[0]); 773d4f54a18SHanjun Guo if (!iort_fwnode) 774d4f54a18SHanjun Guo return NULL; 775d4f54a18SHanjun Guo 776d4f54a18SHanjun Guo return irq_find_matching_fwnode(iort_fwnode, DOMAIN_BUS_PLATFORM_MSI); 777d4f54a18SHanjun Guo } 778d4f54a18SHanjun Guo 779d4f54a18SHanjun Guo void acpi_configure_pmsi_domain(struct device *dev) 780d4f54a18SHanjun Guo { 781d4f54a18SHanjun Guo struct irq_domain *msi_domain; 782d4f54a18SHanjun Guo 783d4f54a18SHanjun Guo msi_domain = iort_get_platform_device_domain(dev); 784d4f54a18SHanjun Guo if (msi_domain) 785d4f54a18SHanjun Guo dev_set_msi_domain(dev, msi_domain); 786d4f54a18SHanjun Guo } 787d4f54a18SHanjun Guo 788d49f2dedSLorenzo Pieralisi #ifdef CONFIG_IOMMU_API 7898b4282e6SShameer Kolothum static struct acpi_iort_node *iort_get_msi_resv_iommu(struct device *dev) 7908b4282e6SShameer Kolothum { 7918b4282e6SShameer Kolothum struct acpi_iort_node *iommu; 7928097e53eSJoerg Roedel struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); 7938b4282e6SShameer Kolothum 7948b4282e6SShameer Kolothum iommu = iort_get_iort_node(fwspec->iommu_fwnode); 7958b4282e6SShameer Kolothum 7968b4282e6SShameer Kolothum if (iommu && (iommu->type == ACPI_IORT_NODE_SMMU_V3)) { 7978b4282e6SShameer Kolothum struct acpi_iort_smmu_v3 *smmu; 7988b4282e6SShameer Kolothum 7998b4282e6SShameer Kolothum smmu = (struct acpi_iort_smmu_v3 *)iommu->node_data; 8008b4282e6SShameer Kolothum if (smmu->model == ACPI_IORT_SMMU_V3_HISILICON_HI161X) 8018b4282e6SShameer Kolothum return iommu; 8028b4282e6SShameer Kolothum } 8038b4282e6SShameer Kolothum 8048b4282e6SShameer Kolothum return NULL; 8058b4282e6SShameer Kolothum } 8068b4282e6SShameer Kolothum 8078097e53eSJoerg Roedel static inline const struct iommu_ops *iort_fwspec_iommu_ops(struct device *dev) 808d49f2dedSLorenzo Pieralisi { 8098097e53eSJoerg Roedel struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); 8108097e53eSJoerg Roedel 811d49f2dedSLorenzo Pieralisi return (fwspec && fwspec->ops) ? fwspec->ops : NULL; 812d49f2dedSLorenzo Pieralisi } 813d49f2dedSLorenzo Pieralisi 814e3d49392SLorenzo Pieralisi static inline int iort_add_device_replay(const struct iommu_ops *ops, 815e3d49392SLorenzo Pieralisi struct device *dev) 816d49f2dedSLorenzo Pieralisi { 817d49f2dedSLorenzo Pieralisi int err = 0; 818d49f2dedSLorenzo Pieralisi 819d2e1a003SJoerg Roedel if (dev->bus && !device_iommu_mapped(dev)) 820d2e1a003SJoerg Roedel err = iommu_probe_device(dev); 821d49f2dedSLorenzo Pieralisi 822d49f2dedSLorenzo Pieralisi return err; 823d49f2dedSLorenzo Pieralisi } 8248b4282e6SShameer Kolothum 8258b4282e6SShameer Kolothum /** 8268b4282e6SShameer Kolothum * iort_iommu_msi_get_resv_regions - Reserved region driver helper 8278b4282e6SShameer Kolothum * @dev: Device from iommu_get_resv_regions() 8288b4282e6SShameer Kolothum * @head: Reserved region list from iommu_get_resv_regions() 8298b4282e6SShameer Kolothum * 8308b4282e6SShameer Kolothum * Returns: Number of msi reserved regions on success (0 if platform 8318b4282e6SShameer Kolothum * doesn't require the reservation or no associated msi regions), 8328b4282e6SShameer Kolothum * appropriate error value otherwise. The ITS interrupt translation 8338b4282e6SShameer Kolothum * spaces (ITS_base + SZ_64K, SZ_64K) associated with the device 8348b4282e6SShameer Kolothum * are the msi reserved regions. 8358b4282e6SShameer Kolothum */ 8368b4282e6SShameer Kolothum int iort_iommu_msi_get_resv_regions(struct device *dev, struct list_head *head) 8378b4282e6SShameer Kolothum { 8388097e53eSJoerg Roedel struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); 8398b4282e6SShameer Kolothum struct acpi_iort_its_group *its; 8408b4282e6SShameer Kolothum struct acpi_iort_node *iommu_node, *its_node = NULL; 8418b4282e6SShameer Kolothum int i, resv = 0; 8428b4282e6SShameer Kolothum 8438b4282e6SShameer Kolothum iommu_node = iort_get_msi_resv_iommu(dev); 8448b4282e6SShameer Kolothum if (!iommu_node) 8458b4282e6SShameer Kolothum return 0; 8468b4282e6SShameer Kolothum 8478b4282e6SShameer Kolothum /* 8488b4282e6SShameer Kolothum * Current logic to reserve ITS regions relies on HW topologies 8498b4282e6SShameer Kolothum * where a given PCI or named component maps its IDs to only one 8508b4282e6SShameer Kolothum * ITS group; if a PCI or named component can map its IDs to 8518b4282e6SShameer Kolothum * different ITS groups through IORT mappings this function has 8528b4282e6SShameer Kolothum * to be reworked to ensure we reserve regions for all ITS groups 8538b4282e6SShameer Kolothum * a given PCI or named component may map IDs to. 8548b4282e6SShameer Kolothum */ 8558b4282e6SShameer Kolothum 8568097e53eSJoerg Roedel for (i = 0; i < fwspec->num_ids; i++) { 8578b4282e6SShameer Kolothum its_node = iort_node_map_id(iommu_node, 8588097e53eSJoerg Roedel fwspec->ids[i], 8598b4282e6SShameer Kolothum NULL, IORT_MSI_TYPE); 8608b4282e6SShameer Kolothum if (its_node) 8618b4282e6SShameer Kolothum break; 8628b4282e6SShameer Kolothum } 8638b4282e6SShameer Kolothum 8648b4282e6SShameer Kolothum if (!its_node) 8658b4282e6SShameer Kolothum return 0; 8668b4282e6SShameer Kolothum 8678b4282e6SShameer Kolothum /* Move to ITS specific data */ 8688b4282e6SShameer Kolothum its = (struct acpi_iort_its_group *)its_node->node_data; 8698b4282e6SShameer Kolothum 8708b4282e6SShameer Kolothum for (i = 0; i < its->its_count; i++) { 8718b4282e6SShameer Kolothum phys_addr_t base; 8728b4282e6SShameer Kolothum 8738b4282e6SShameer Kolothum if (!iort_find_its_base(its->identifiers[i], &base)) { 8748b4282e6SShameer Kolothum int prot = IOMMU_WRITE | IOMMU_NOEXEC | IOMMU_MMIO; 8758b4282e6SShameer Kolothum struct iommu_resv_region *region; 8768b4282e6SShameer Kolothum 8778b4282e6SShameer Kolothum region = iommu_alloc_resv_region(base + SZ_64K, SZ_64K, 8788b4282e6SShameer Kolothum prot, IOMMU_RESV_MSI); 8798b4282e6SShameer Kolothum if (region) { 8808b4282e6SShameer Kolothum list_add_tail(®ion->list, head); 8818b4282e6SShameer Kolothum resv++; 8828b4282e6SShameer Kolothum } 8838b4282e6SShameer Kolothum } 8848b4282e6SShameer Kolothum } 8858b4282e6SShameer Kolothum 8868b4282e6SShameer Kolothum return (resv == its->its_count) ? resv : -ENODEV; 8878b4282e6SShameer Kolothum } 88882126886SLorenzo Pieralisi 88982126886SLorenzo Pieralisi static inline bool iort_iommu_driver_enabled(u8 type) 89082126886SLorenzo Pieralisi { 89182126886SLorenzo Pieralisi switch (type) { 89282126886SLorenzo Pieralisi case ACPI_IORT_NODE_SMMU_V3: 893d3daf666SArd Biesheuvel return IS_ENABLED(CONFIG_ARM_SMMU_V3); 89482126886SLorenzo Pieralisi case ACPI_IORT_NODE_SMMU: 895d3daf666SArd Biesheuvel return IS_ENABLED(CONFIG_ARM_SMMU); 89682126886SLorenzo Pieralisi default: 89782126886SLorenzo Pieralisi pr_warn("IORT node type %u does not describe an SMMU\n", type); 89882126886SLorenzo Pieralisi return false; 89982126886SLorenzo Pieralisi } 90082126886SLorenzo Pieralisi } 90182126886SLorenzo Pieralisi 90282126886SLorenzo Pieralisi static int arm_smmu_iort_xlate(struct device *dev, u32 streamid, 90382126886SLorenzo Pieralisi struct fwnode_handle *fwnode, 90482126886SLorenzo Pieralisi const struct iommu_ops *ops) 90582126886SLorenzo Pieralisi { 90682126886SLorenzo Pieralisi int ret = iommu_fwspec_init(dev, fwnode, ops); 90782126886SLorenzo Pieralisi 90882126886SLorenzo Pieralisi if (!ret) 90982126886SLorenzo Pieralisi ret = iommu_fwspec_add_ids(dev, &streamid, 1); 91082126886SLorenzo Pieralisi 91182126886SLorenzo Pieralisi return ret; 91282126886SLorenzo Pieralisi } 91382126886SLorenzo Pieralisi 91482126886SLorenzo Pieralisi static bool iort_pci_rc_supports_ats(struct acpi_iort_node *node) 91582126886SLorenzo Pieralisi { 91682126886SLorenzo Pieralisi struct acpi_iort_root_complex *pci_rc; 91782126886SLorenzo Pieralisi 91882126886SLorenzo Pieralisi pci_rc = (struct acpi_iort_root_complex *)node->node_data; 91982126886SLorenzo Pieralisi return pci_rc->ats_attribute & ACPI_IORT_ATS_SUPPORTED; 92082126886SLorenzo Pieralisi } 921d49f2dedSLorenzo Pieralisi 922bc8648d4SRobin Murphy static int iort_iommu_xlate(struct device *dev, struct acpi_iort_node *node, 923643b8e4dSLorenzo Pieralisi u32 streamid) 924643b8e4dSLorenzo Pieralisi { 925bc8648d4SRobin Murphy const struct iommu_ops *ops; 926643b8e4dSLorenzo Pieralisi struct fwnode_handle *iort_fwnode; 927643b8e4dSLorenzo Pieralisi 928bc8648d4SRobin Murphy if (!node) 929bc8648d4SRobin Murphy return -ENODEV; 930bc8648d4SRobin Murphy 931643b8e4dSLorenzo Pieralisi iort_fwnode = iort_get_fwnode(node); 932643b8e4dSLorenzo Pieralisi if (!iort_fwnode) 933bc8648d4SRobin Murphy return -ENODEV; 934643b8e4dSLorenzo Pieralisi 9355a1bb638SSricharan R /* 9365a1bb638SSricharan R * If the ops look-up fails, this means that either 9375a1bb638SSricharan R * the SMMU drivers have not been probed yet or that 9385a1bb638SSricharan R * the SMMU drivers are not built in the kernel; 9395a1bb638SSricharan R * Depending on whether the SMMU drivers are built-in 9405a1bb638SSricharan R * in the kernel or not, defer the IOMMU configuration 9415a1bb638SSricharan R * or just abort it. 9425a1bb638SSricharan R */ 943bc8648d4SRobin Murphy ops = iommu_ops_from_fwnode(iort_fwnode); 944643b8e4dSLorenzo Pieralisi if (!ops) 9455a1bb638SSricharan R return iort_iommu_driver_enabled(node->type) ? 946bc8648d4SRobin Murphy -EPROBE_DEFER : -ENODEV; 947643b8e4dSLorenzo Pieralisi 948bc8648d4SRobin Murphy return arm_smmu_iort_xlate(dev, streamid, iort_fwnode, ops); 949643b8e4dSLorenzo Pieralisi } 950643b8e4dSLorenzo Pieralisi 951bc8648d4SRobin Murphy struct iort_pci_alias_info { 952bc8648d4SRobin Murphy struct device *dev; 953bc8648d4SRobin Murphy struct acpi_iort_node *node; 954bc8648d4SRobin Murphy }; 955bc8648d4SRobin Murphy 956bc8648d4SRobin Murphy static int iort_pci_iommu_init(struct pci_dev *pdev, u16 alias, void *data) 957bc8648d4SRobin Murphy { 958bc8648d4SRobin Murphy struct iort_pci_alias_info *info = data; 959bc8648d4SRobin Murphy struct acpi_iort_node *parent; 960bc8648d4SRobin Murphy u32 streamid; 961bc8648d4SRobin Murphy 962bc8648d4SRobin Murphy parent = iort_node_map_id(info->node, alias, &streamid, 963bc8648d4SRobin Murphy IORT_IOMMU_TYPE); 964bc8648d4SRobin Murphy return iort_iommu_xlate(info->dev, parent, streamid); 965643b8e4dSLorenzo Pieralisi } 966643b8e4dSLorenzo Pieralisi 967da22565dSJean-Philippe Brucker static void iort_named_component_init(struct device *dev, 968da22565dSJean-Philippe Brucker struct acpi_iort_node *node) 969da22565dSJean-Philippe Brucker { 970da22565dSJean-Philippe Brucker struct acpi_iort_named_component *nc; 971da22565dSJean-Philippe Brucker struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); 972da22565dSJean-Philippe Brucker 973da22565dSJean-Philippe Brucker if (!fwspec) 974da22565dSJean-Philippe Brucker return; 975da22565dSJean-Philippe Brucker 976da22565dSJean-Philippe Brucker nc = (struct acpi_iort_named_component *)node->node_data; 977da22565dSJean-Philippe Brucker fwspec->num_pasid_bits = FIELD_GET(ACPI_IORT_NC_PASID_BITS, 978da22565dSJean-Philippe Brucker nc->node_flags); 979da22565dSJean-Philippe Brucker } 980da22565dSJean-Philippe Brucker 981b8e069a2SLorenzo Pieralisi static int iort_nc_iommu_map(struct device *dev, struct acpi_iort_node *node) 982b8e069a2SLorenzo Pieralisi { 983b8e069a2SLorenzo Pieralisi struct acpi_iort_node *parent; 984b8e069a2SLorenzo Pieralisi int err = -ENODEV, i = 0; 985b8e069a2SLorenzo Pieralisi u32 streamid = 0; 986b8e069a2SLorenzo Pieralisi 987b8e069a2SLorenzo Pieralisi do { 988b8e069a2SLorenzo Pieralisi 989b8e069a2SLorenzo Pieralisi parent = iort_node_map_platform_id(node, &streamid, 990b8e069a2SLorenzo Pieralisi IORT_IOMMU_TYPE, 991b8e069a2SLorenzo Pieralisi i++); 992b8e069a2SLorenzo Pieralisi 993b8e069a2SLorenzo Pieralisi if (parent) 994b8e069a2SLorenzo Pieralisi err = iort_iommu_xlate(dev, parent, streamid); 995b8e069a2SLorenzo Pieralisi } while (parent && !err); 996b8e069a2SLorenzo Pieralisi 997b8e069a2SLorenzo Pieralisi return err; 998b8e069a2SLorenzo Pieralisi } 999b8e069a2SLorenzo Pieralisi 1000b8e069a2SLorenzo Pieralisi static int iort_nc_iommu_map_id(struct device *dev, 1001b8e069a2SLorenzo Pieralisi struct acpi_iort_node *node, 1002b8e069a2SLorenzo Pieralisi const u32 *in_id) 1003b8e069a2SLorenzo Pieralisi { 1004b8e069a2SLorenzo Pieralisi struct acpi_iort_node *parent; 1005b8e069a2SLorenzo Pieralisi u32 streamid; 1006b8e069a2SLorenzo Pieralisi 1007b8e069a2SLorenzo Pieralisi parent = iort_node_map_id(node, *in_id, &streamid, IORT_IOMMU_TYPE); 1008b8e069a2SLorenzo Pieralisi if (parent) 1009b8e069a2SLorenzo Pieralisi return iort_iommu_xlate(dev, parent, streamid); 1010b8e069a2SLorenzo Pieralisi 1011b8e069a2SLorenzo Pieralisi return -ENODEV; 1012b8e069a2SLorenzo Pieralisi } 1013b8e069a2SLorenzo Pieralisi 1014b8e069a2SLorenzo Pieralisi 101582126886SLorenzo Pieralisi /** 1016b8e069a2SLorenzo Pieralisi * iort_iommu_configure_id - Set-up IOMMU configuration for a device. 101782126886SLorenzo Pieralisi * 101882126886SLorenzo Pieralisi * @dev: device to configure 1019b8e069a2SLorenzo Pieralisi * @id_in: optional input id const value pointer 102082126886SLorenzo Pieralisi * 102182126886SLorenzo Pieralisi * Returns: iommu_ops pointer on configuration success 102282126886SLorenzo Pieralisi * NULL on configuration failure 102382126886SLorenzo Pieralisi */ 1024b8e069a2SLorenzo Pieralisi const struct iommu_ops *iort_iommu_configure_id(struct device *dev, 1025b8e069a2SLorenzo Pieralisi const u32 *id_in) 102682126886SLorenzo Pieralisi { 1027b8e069a2SLorenzo Pieralisi struct acpi_iort_node *node; 102882126886SLorenzo Pieralisi const struct iommu_ops *ops; 102982126886SLorenzo Pieralisi int err = -ENODEV; 103082126886SLorenzo Pieralisi 103182126886SLorenzo Pieralisi /* 103282126886SLorenzo Pieralisi * If we already translated the fwspec there 103382126886SLorenzo Pieralisi * is nothing left to do, return the iommu_ops. 103482126886SLorenzo Pieralisi */ 103582126886SLorenzo Pieralisi ops = iort_fwspec_iommu_ops(dev); 103682126886SLorenzo Pieralisi if (ops) 103782126886SLorenzo Pieralisi return ops; 103882126886SLorenzo Pieralisi 103982126886SLorenzo Pieralisi if (dev_is_pci(dev)) { 10406990ec79SJoerg Roedel struct iommu_fwspec *fwspec; 104182126886SLorenzo Pieralisi struct pci_bus *bus = to_pci_dev(dev)->bus; 104282126886SLorenzo Pieralisi struct iort_pci_alias_info info = { .dev = dev }; 104382126886SLorenzo Pieralisi 104482126886SLorenzo Pieralisi node = iort_scan_node(ACPI_IORT_NODE_PCI_ROOT_COMPLEX, 104582126886SLorenzo Pieralisi iort_match_node_callback, &bus->dev); 104682126886SLorenzo Pieralisi if (!node) 104782126886SLorenzo Pieralisi return NULL; 104882126886SLorenzo Pieralisi 104982126886SLorenzo Pieralisi info.node = node; 105082126886SLorenzo Pieralisi err = pci_for_each_dma_alias(to_pci_dev(dev), 105182126886SLorenzo Pieralisi iort_pci_iommu_init, &info); 105282126886SLorenzo Pieralisi 10536990ec79SJoerg Roedel fwspec = dev_iommu_fwspec_get(dev); 10546990ec79SJoerg Roedel if (fwspec && iort_pci_rc_supports_ats(node)) 10556990ec79SJoerg Roedel fwspec->flags |= IOMMU_FWSPEC_PCI_RC_ATS; 105682126886SLorenzo Pieralisi } else { 105782126886SLorenzo Pieralisi node = iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT, 105882126886SLorenzo Pieralisi iort_match_node_callback, dev); 105982126886SLorenzo Pieralisi if (!node) 106082126886SLorenzo Pieralisi return NULL; 106182126886SLorenzo Pieralisi 1062b8e069a2SLorenzo Pieralisi err = id_in ? iort_nc_iommu_map_id(dev, node, id_in) : 1063b8e069a2SLorenzo Pieralisi iort_nc_iommu_map(dev, node); 1064da22565dSJean-Philippe Brucker 1065da22565dSJean-Philippe Brucker if (!err) 1066da22565dSJean-Philippe Brucker iort_named_component_init(dev, node); 106782126886SLorenzo Pieralisi } 106882126886SLorenzo Pieralisi 106982126886SLorenzo Pieralisi /* 107082126886SLorenzo Pieralisi * If we have reason to believe the IOMMU driver missed the initial 107182126886SLorenzo Pieralisi * add_device callback for dev, replay it to get things in order. 107282126886SLorenzo Pieralisi */ 107382126886SLorenzo Pieralisi if (!err) { 107482126886SLorenzo Pieralisi ops = iort_fwspec_iommu_ops(dev); 107582126886SLorenzo Pieralisi err = iort_add_device_replay(ops, dev); 107682126886SLorenzo Pieralisi } 107782126886SLorenzo Pieralisi 107882126886SLorenzo Pieralisi /* Ignore all other errors apart from EPROBE_DEFER */ 107982126886SLorenzo Pieralisi if (err == -EPROBE_DEFER) { 108082126886SLorenzo Pieralisi ops = ERR_PTR(err); 108182126886SLorenzo Pieralisi } else if (err) { 108282126886SLorenzo Pieralisi dev_dbg(dev, "Adding to IOMMU failed: %d\n", err); 108382126886SLorenzo Pieralisi ops = NULL; 108482126886SLorenzo Pieralisi } 108582126886SLorenzo Pieralisi 108682126886SLorenzo Pieralisi return ops; 108782126886SLorenzo Pieralisi } 1088b8e069a2SLorenzo Pieralisi 108982126886SLorenzo Pieralisi #else 109082126886SLorenzo Pieralisi static inline const struct iommu_ops *iort_fwspec_iommu_ops(struct device *dev) 109182126886SLorenzo Pieralisi { return NULL; } 109282126886SLorenzo Pieralisi static inline int iort_add_device_replay(const struct iommu_ops *ops, 109382126886SLorenzo Pieralisi struct device *dev) 109482126886SLorenzo Pieralisi { return 0; } 109582126886SLorenzo Pieralisi int iort_iommu_msi_get_resv_regions(struct device *dev, struct list_head *head) 109682126886SLorenzo Pieralisi { return 0; } 1097b8e069a2SLorenzo Pieralisi const struct iommu_ops *iort_iommu_configure_id(struct device *dev, 1098b8e069a2SLorenzo Pieralisi const u32 *input_id) 109982126886SLorenzo Pieralisi { return NULL; } 110082126886SLorenzo Pieralisi #endif 110182126886SLorenzo Pieralisi 110210d8ab2cSLorenzo Pieralisi static int nc_dma_get_range(struct device *dev, u64 *size) 110310d8ab2cSLorenzo Pieralisi { 110410d8ab2cSLorenzo Pieralisi struct acpi_iort_node *node; 110510d8ab2cSLorenzo Pieralisi struct acpi_iort_named_component *ncomp; 110610d8ab2cSLorenzo Pieralisi 110710d8ab2cSLorenzo Pieralisi node = iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT, 110810d8ab2cSLorenzo Pieralisi iort_match_node_callback, dev); 110910d8ab2cSLorenzo Pieralisi if (!node) 111010d8ab2cSLorenzo Pieralisi return -ENODEV; 111110d8ab2cSLorenzo Pieralisi 111210d8ab2cSLorenzo Pieralisi ncomp = (struct acpi_iort_named_component *)node->node_data; 111310d8ab2cSLorenzo Pieralisi 111410d8ab2cSLorenzo Pieralisi *size = ncomp->memory_address_limit >= 64 ? U64_MAX : 111510d8ab2cSLorenzo Pieralisi 1ULL<<ncomp->memory_address_limit; 111610d8ab2cSLorenzo Pieralisi 111710d8ab2cSLorenzo Pieralisi return 0; 111810d8ab2cSLorenzo Pieralisi } 111910d8ab2cSLorenzo Pieralisi 11205ac65e8cSRobin Murphy static int rc_dma_get_range(struct device *dev, u64 *size) 11215ac65e8cSRobin Murphy { 11225ac65e8cSRobin Murphy struct acpi_iort_node *node; 11235ac65e8cSRobin Murphy struct acpi_iort_root_complex *rc; 1124c7777236SJean-Philippe Brucker struct pci_bus *pbus = to_pci_dev(dev)->bus; 11255ac65e8cSRobin Murphy 11265ac65e8cSRobin Murphy node = iort_scan_node(ACPI_IORT_NODE_PCI_ROOT_COMPLEX, 1127c7777236SJean-Philippe Brucker iort_match_node_callback, &pbus->dev); 11285ac65e8cSRobin Murphy if (!node || node->revision < 1) 11295ac65e8cSRobin Murphy return -ENODEV; 11305ac65e8cSRobin Murphy 11315ac65e8cSRobin Murphy rc = (struct acpi_iort_root_complex *)node->node_data; 11325ac65e8cSRobin Murphy 11335ac65e8cSRobin Murphy *size = rc->memory_address_limit >= 64 ? U64_MAX : 11345ac65e8cSRobin Murphy 1ULL<<rc->memory_address_limit; 11355ac65e8cSRobin Murphy 11365ac65e8cSRobin Murphy return 0; 11375ac65e8cSRobin Murphy } 11385ac65e8cSRobin Murphy 1139643b8e4dSLorenzo Pieralisi /** 11407ad42639SLorenzo Pieralisi * iort_dma_setup() - Set-up device DMA parameters. 114118b709beSLorenzo Pieralisi * 114218b709beSLorenzo Pieralisi * @dev: device to configure 11437ad42639SLorenzo Pieralisi * @dma_addr: device DMA address result pointer 11447ad42639SLorenzo Pieralisi * @size: DMA range size result pointer 114518b709beSLorenzo Pieralisi */ 11467ad42639SLorenzo Pieralisi void iort_dma_setup(struct device *dev, u64 *dma_addr, u64 *dma_size) 114718b709beSLorenzo Pieralisi { 1148a7ba70f1SNicolas Saenz Julienne u64 end, mask, dmaaddr = 0, size = 0, offset = 0; 1149a7ba70f1SNicolas Saenz Julienne int ret; 11507ad42639SLorenzo Pieralisi 115118b709beSLorenzo Pieralisi /* 11526757cdaeSRobin Murphy * If @dev is expected to be DMA-capable then the bus code that created 11536757cdaeSRobin Murphy * it should have initialised its dma_mask pointer by this point. For 11546757cdaeSRobin Murphy * now, we'll continue the legacy behaviour of coercing it to the 11556757cdaeSRobin Murphy * coherent mask if not, but we'll no longer do so quietly. 115618b709beSLorenzo Pieralisi */ 11576757cdaeSRobin Murphy if (!dev->dma_mask) { 11586757cdaeSRobin Murphy dev_warn(dev, "DMA mask not set\n"); 115918b709beSLorenzo Pieralisi dev->dma_mask = &dev->coherent_dma_mask; 11606757cdaeSRobin Murphy } 11617ad42639SLorenzo Pieralisi 11626757cdaeSRobin Murphy if (dev->coherent_dma_mask) 11637ad42639SLorenzo Pieralisi size = max(dev->coherent_dma_mask, dev->coherent_dma_mask + 1); 11646757cdaeSRobin Murphy else 11656757cdaeSRobin Murphy size = 1ULL << 32; 11667ad42639SLorenzo Pieralisi 11677ad42639SLorenzo Pieralisi ret = acpi_dma_get_range(dev, &dmaaddr, &offset, &size); 11685ac65e8cSRobin Murphy if (ret == -ENODEV) 11697fb89e1dSArd Biesheuvel ret = dev_is_pci(dev) ? rc_dma_get_range(dev, &size) 11707fb89e1dSArd Biesheuvel : nc_dma_get_range(dev, &size); 117110d8ab2cSLorenzo Pieralisi 11727ad42639SLorenzo Pieralisi if (!ret) { 11737ad42639SLorenzo Pieralisi /* 1174a7ba70f1SNicolas Saenz Julienne * Limit coherent and dma mask based on size retrieved from 1175a7ba70f1SNicolas Saenz Julienne * firmware. 11767ad42639SLorenzo Pieralisi */ 1177a7ba70f1SNicolas Saenz Julienne end = dmaaddr + size - 1; 1178a7ba70f1SNicolas Saenz Julienne mask = DMA_BIT_MASK(ilog2(end) + 1); 1179a7ba70f1SNicolas Saenz Julienne dev->bus_dma_limit = end; 11807ad42639SLorenzo Pieralisi dev->coherent_dma_mask = mask; 11817ad42639SLorenzo Pieralisi *dev->dma_mask = mask; 11827ad42639SLorenzo Pieralisi } 11837ad42639SLorenzo Pieralisi 11847ad42639SLorenzo Pieralisi *dma_addr = dmaaddr; 11857ad42639SLorenzo Pieralisi *dma_size = size; 11867ad42639SLorenzo Pieralisi 11877ad42639SLorenzo Pieralisi dev->dma_pfn_offset = PFN_DOWN(offset); 11887ad42639SLorenzo Pieralisi dev_dbg(dev, "dma_pfn_offset(%#08llx)\n", offset); 118918b709beSLorenzo Pieralisi } 119018b709beSLorenzo Pieralisi 1191e4dadfa8SLorenzo Pieralisi static void __init acpi_iort_register_irq(int hwirq, const char *name, 1192e4dadfa8SLorenzo Pieralisi int trigger, 1193e4dadfa8SLorenzo Pieralisi struct resource *res) 1194e4dadfa8SLorenzo Pieralisi { 1195e4dadfa8SLorenzo Pieralisi int irq = acpi_register_gsi(NULL, hwirq, trigger, 1196e4dadfa8SLorenzo Pieralisi ACPI_ACTIVE_HIGH); 1197e4dadfa8SLorenzo Pieralisi 1198e4dadfa8SLorenzo Pieralisi if (irq <= 0) { 1199e4dadfa8SLorenzo Pieralisi pr_err("could not register gsi hwirq %d name [%s]\n", hwirq, 1200e4dadfa8SLorenzo Pieralisi name); 1201e4dadfa8SLorenzo Pieralisi return; 1202e4dadfa8SLorenzo Pieralisi } 1203e4dadfa8SLorenzo Pieralisi 1204e4dadfa8SLorenzo Pieralisi res->start = irq; 1205e4dadfa8SLorenzo Pieralisi res->end = irq; 1206e4dadfa8SLorenzo Pieralisi res->flags = IORESOURCE_IRQ; 1207e4dadfa8SLorenzo Pieralisi res->name = name; 1208e4dadfa8SLorenzo Pieralisi } 1209e4dadfa8SLorenzo Pieralisi 1210e4dadfa8SLorenzo Pieralisi static int __init arm_smmu_v3_count_resources(struct acpi_iort_node *node) 1211e4dadfa8SLorenzo Pieralisi { 1212e4dadfa8SLorenzo Pieralisi struct acpi_iort_smmu_v3 *smmu; 1213e4dadfa8SLorenzo Pieralisi /* Always present mem resource */ 1214e4dadfa8SLorenzo Pieralisi int num_res = 1; 1215e4dadfa8SLorenzo Pieralisi 1216e4dadfa8SLorenzo Pieralisi /* Retrieve SMMUv3 specific data */ 1217e4dadfa8SLorenzo Pieralisi smmu = (struct acpi_iort_smmu_v3 *)node->node_data; 1218e4dadfa8SLorenzo Pieralisi 1219e4dadfa8SLorenzo Pieralisi if (smmu->event_gsiv) 1220e4dadfa8SLorenzo Pieralisi num_res++; 1221e4dadfa8SLorenzo Pieralisi 1222e4dadfa8SLorenzo Pieralisi if (smmu->pri_gsiv) 1223e4dadfa8SLorenzo Pieralisi num_res++; 1224e4dadfa8SLorenzo Pieralisi 1225e4dadfa8SLorenzo Pieralisi if (smmu->gerr_gsiv) 1226e4dadfa8SLorenzo Pieralisi num_res++; 1227e4dadfa8SLorenzo Pieralisi 1228e4dadfa8SLorenzo Pieralisi if (smmu->sync_gsiv) 1229e4dadfa8SLorenzo Pieralisi num_res++; 1230e4dadfa8SLorenzo Pieralisi 1231e4dadfa8SLorenzo Pieralisi return num_res; 1232e4dadfa8SLorenzo Pieralisi } 1233e4dadfa8SLorenzo Pieralisi 1234f935448aSGeetha Sowjanya static bool arm_smmu_v3_is_combined_irq(struct acpi_iort_smmu_v3 *smmu) 1235f935448aSGeetha Sowjanya { 1236f935448aSGeetha Sowjanya /* 1237f935448aSGeetha Sowjanya * Cavium ThunderX2 implementation doesn't not support unique 1238f935448aSGeetha Sowjanya * irq line. Use single irq line for all the SMMUv3 interrupts. 1239f935448aSGeetha Sowjanya */ 1240f935448aSGeetha Sowjanya if (smmu->model != ACPI_IORT_SMMU_V3_CAVIUM_CN99XX) 1241f935448aSGeetha Sowjanya return false; 1242f935448aSGeetha Sowjanya 1243f935448aSGeetha Sowjanya /* 1244f935448aSGeetha Sowjanya * ThunderX2 doesn't support MSIs from the SMMU, so we're checking 1245f935448aSGeetha Sowjanya * SPI numbers here. 1246f935448aSGeetha Sowjanya */ 1247f935448aSGeetha Sowjanya return smmu->event_gsiv == smmu->pri_gsiv && 1248f935448aSGeetha Sowjanya smmu->event_gsiv == smmu->gerr_gsiv && 1249f935448aSGeetha Sowjanya smmu->event_gsiv == smmu->sync_gsiv; 1250f935448aSGeetha Sowjanya } 1251f935448aSGeetha Sowjanya 1252403e8c7cSLinu Cherian static unsigned long arm_smmu_v3_resource_size(struct acpi_iort_smmu_v3 *smmu) 1253403e8c7cSLinu Cherian { 1254403e8c7cSLinu Cherian /* 1255403e8c7cSLinu Cherian * Override the size, for Cavium ThunderX2 implementation 1256403e8c7cSLinu Cherian * which doesn't support the page 1 SMMU register space. 1257403e8c7cSLinu Cherian */ 1258403e8c7cSLinu Cherian if (smmu->model == ACPI_IORT_SMMU_V3_CAVIUM_CN99XX) 1259403e8c7cSLinu Cherian return SZ_64K; 1260403e8c7cSLinu Cherian 1261403e8c7cSLinu Cherian return SZ_128K; 1262403e8c7cSLinu Cherian } 1263403e8c7cSLinu Cherian 1264e4dadfa8SLorenzo Pieralisi static void __init arm_smmu_v3_init_resources(struct resource *res, 1265e4dadfa8SLorenzo Pieralisi struct acpi_iort_node *node) 1266e4dadfa8SLorenzo Pieralisi { 1267e4dadfa8SLorenzo Pieralisi struct acpi_iort_smmu_v3 *smmu; 1268e4dadfa8SLorenzo Pieralisi int num_res = 0; 1269e4dadfa8SLorenzo Pieralisi 1270e4dadfa8SLorenzo Pieralisi /* Retrieve SMMUv3 specific data */ 1271e4dadfa8SLorenzo Pieralisi smmu = (struct acpi_iort_smmu_v3 *)node->node_data; 1272e4dadfa8SLorenzo Pieralisi 1273e4dadfa8SLorenzo Pieralisi res[num_res].start = smmu->base_address; 1274403e8c7cSLinu Cherian res[num_res].end = smmu->base_address + 1275403e8c7cSLinu Cherian arm_smmu_v3_resource_size(smmu) - 1; 1276e4dadfa8SLorenzo Pieralisi res[num_res].flags = IORESOURCE_MEM; 1277e4dadfa8SLorenzo Pieralisi 1278e4dadfa8SLorenzo Pieralisi num_res++; 1279f935448aSGeetha Sowjanya if (arm_smmu_v3_is_combined_irq(smmu)) { 1280f935448aSGeetha Sowjanya if (smmu->event_gsiv) 1281f935448aSGeetha Sowjanya acpi_iort_register_irq(smmu->event_gsiv, "combined", 1282f935448aSGeetha Sowjanya ACPI_EDGE_SENSITIVE, 1283f935448aSGeetha Sowjanya &res[num_res++]); 1284f935448aSGeetha Sowjanya } else { 1285e4dadfa8SLorenzo Pieralisi 1286e4dadfa8SLorenzo Pieralisi if (smmu->event_gsiv) 1287e4dadfa8SLorenzo Pieralisi acpi_iort_register_irq(smmu->event_gsiv, "eventq", 1288e4dadfa8SLorenzo Pieralisi ACPI_EDGE_SENSITIVE, 1289e4dadfa8SLorenzo Pieralisi &res[num_res++]); 1290e4dadfa8SLorenzo Pieralisi 1291e4dadfa8SLorenzo Pieralisi if (smmu->pri_gsiv) 1292e4dadfa8SLorenzo Pieralisi acpi_iort_register_irq(smmu->pri_gsiv, "priq", 1293e4dadfa8SLorenzo Pieralisi ACPI_EDGE_SENSITIVE, 1294e4dadfa8SLorenzo Pieralisi &res[num_res++]); 1295e4dadfa8SLorenzo Pieralisi 1296e4dadfa8SLorenzo Pieralisi if (smmu->gerr_gsiv) 1297e4dadfa8SLorenzo Pieralisi acpi_iort_register_irq(smmu->gerr_gsiv, "gerror", 1298e4dadfa8SLorenzo Pieralisi ACPI_EDGE_SENSITIVE, 1299e4dadfa8SLorenzo Pieralisi &res[num_res++]); 1300e4dadfa8SLorenzo Pieralisi 1301e4dadfa8SLorenzo Pieralisi if (smmu->sync_gsiv) 1302e4dadfa8SLorenzo Pieralisi acpi_iort_register_irq(smmu->sync_gsiv, "cmdq-sync", 1303e4dadfa8SLorenzo Pieralisi ACPI_EDGE_SENSITIVE, 1304e4dadfa8SLorenzo Pieralisi &res[num_res++]); 1305e4dadfa8SLorenzo Pieralisi } 1306f935448aSGeetha Sowjanya } 1307e4dadfa8SLorenzo Pieralisi 130824e51604SNeil Leeder static void __init arm_smmu_v3_dma_configure(struct device *dev, 130924e51604SNeil Leeder struct acpi_iort_node *node) 1310e4dadfa8SLorenzo Pieralisi { 1311e4dadfa8SLorenzo Pieralisi struct acpi_iort_smmu_v3 *smmu; 131224e51604SNeil Leeder enum dev_dma_attr attr; 1313e4dadfa8SLorenzo Pieralisi 1314e4dadfa8SLorenzo Pieralisi /* Retrieve SMMUv3 specific data */ 1315e4dadfa8SLorenzo Pieralisi smmu = (struct acpi_iort_smmu_v3 *)node->node_data; 1316e4dadfa8SLorenzo Pieralisi 131724e51604SNeil Leeder attr = (smmu->flags & ACPI_IORT_SMMU_V3_COHACC_OVERRIDE) ? 131824e51604SNeil Leeder DEV_DMA_COHERENT : DEV_DMA_NON_COHERENT; 131924e51604SNeil Leeder 132024e51604SNeil Leeder /* We expect the dma masks to be equivalent for all SMMUv3 set-ups */ 132124e51604SNeil Leeder dev->dma_mask = &dev->coherent_dma_mask; 132224e51604SNeil Leeder 132324e51604SNeil Leeder /* Configure DMA for the page table walker */ 132424e51604SNeil Leeder acpi_dma_configure(dev, attr); 1325e4dadfa8SLorenzo Pieralisi } 1326e4dadfa8SLorenzo Pieralisi 132775808131SLorenzo Pieralisi #if defined(CONFIG_ACPI_NUMA) 13285fe0ce3bSGanapatrao Kulkarni /* 13295fe0ce3bSGanapatrao Kulkarni * set numa proximity domain for smmuv3 device 13305fe0ce3bSGanapatrao Kulkarni */ 133136a2ba07SKefeng Wang static int __init arm_smmu_v3_set_proximity(struct device *dev, 13325fe0ce3bSGanapatrao Kulkarni struct acpi_iort_node *node) 13335fe0ce3bSGanapatrao Kulkarni { 13345fe0ce3bSGanapatrao Kulkarni struct acpi_iort_smmu_v3 *smmu; 13355fe0ce3bSGanapatrao Kulkarni 13365fe0ce3bSGanapatrao Kulkarni smmu = (struct acpi_iort_smmu_v3 *)node->node_data; 13375fe0ce3bSGanapatrao Kulkarni if (smmu->flags & ACPI_IORT_SMMU_V3_PXM_VALID) { 1338*01feba59SJonathan Cameron int dev_node = pxm_to_node(smmu->pxm); 133936a2ba07SKefeng Wang 13403e77eeb7SLorenzo Pieralisi if (dev_node != NUMA_NO_NODE && !node_online(dev_node)) 134136a2ba07SKefeng Wang return -EINVAL; 134236a2ba07SKefeng Wang 13433e77eeb7SLorenzo Pieralisi set_dev_node(dev, dev_node); 13445fe0ce3bSGanapatrao Kulkarni pr_info("SMMU-v3[%llx] Mapped to Proximity domain %d\n", 13455fe0ce3bSGanapatrao Kulkarni smmu->base_address, 13465fe0ce3bSGanapatrao Kulkarni smmu->pxm); 13475fe0ce3bSGanapatrao Kulkarni } 134836a2ba07SKefeng Wang return 0; 13495fe0ce3bSGanapatrao Kulkarni } 13505fe0ce3bSGanapatrao Kulkarni #else 13515fe0ce3bSGanapatrao Kulkarni #define arm_smmu_v3_set_proximity NULL 13525fe0ce3bSGanapatrao Kulkarni #endif 13535fe0ce3bSGanapatrao Kulkarni 1354d6fcd3b1SLorenzo Pieralisi static int __init arm_smmu_count_resources(struct acpi_iort_node *node) 1355d6fcd3b1SLorenzo Pieralisi { 1356d6fcd3b1SLorenzo Pieralisi struct acpi_iort_smmu *smmu; 1357d6fcd3b1SLorenzo Pieralisi 1358d6fcd3b1SLorenzo Pieralisi /* Retrieve SMMU specific data */ 1359d6fcd3b1SLorenzo Pieralisi smmu = (struct acpi_iort_smmu *)node->node_data; 1360d6fcd3b1SLorenzo Pieralisi 1361d6fcd3b1SLorenzo Pieralisi /* 1362d6fcd3b1SLorenzo Pieralisi * Only consider the global fault interrupt and ignore the 1363d6fcd3b1SLorenzo Pieralisi * configuration access interrupt. 1364d6fcd3b1SLorenzo Pieralisi * 1365d6fcd3b1SLorenzo Pieralisi * MMIO address and global fault interrupt resources are always 1366d6fcd3b1SLorenzo Pieralisi * present so add them to the context interrupt count as a static 1367d6fcd3b1SLorenzo Pieralisi * value. 1368d6fcd3b1SLorenzo Pieralisi */ 1369d6fcd3b1SLorenzo Pieralisi return smmu->context_interrupt_count + 2; 1370d6fcd3b1SLorenzo Pieralisi } 1371d6fcd3b1SLorenzo Pieralisi 1372d6fcd3b1SLorenzo Pieralisi static void __init arm_smmu_init_resources(struct resource *res, 1373d6fcd3b1SLorenzo Pieralisi struct acpi_iort_node *node) 1374d6fcd3b1SLorenzo Pieralisi { 1375d6fcd3b1SLorenzo Pieralisi struct acpi_iort_smmu *smmu; 1376d6fcd3b1SLorenzo Pieralisi int i, hw_irq, trigger, num_res = 0; 1377d6fcd3b1SLorenzo Pieralisi u64 *ctx_irq, *glb_irq; 1378d6fcd3b1SLorenzo Pieralisi 1379d6fcd3b1SLorenzo Pieralisi /* Retrieve SMMU specific data */ 1380d6fcd3b1SLorenzo Pieralisi smmu = (struct acpi_iort_smmu *)node->node_data; 1381d6fcd3b1SLorenzo Pieralisi 1382d6fcd3b1SLorenzo Pieralisi res[num_res].start = smmu->base_address; 1383d6fcd3b1SLorenzo Pieralisi res[num_res].end = smmu->base_address + smmu->span - 1; 1384d6fcd3b1SLorenzo Pieralisi res[num_res].flags = IORESOURCE_MEM; 1385d6fcd3b1SLorenzo Pieralisi num_res++; 1386d6fcd3b1SLorenzo Pieralisi 1387d6fcd3b1SLorenzo Pieralisi glb_irq = ACPI_ADD_PTR(u64, node, smmu->global_interrupt_offset); 1388d6fcd3b1SLorenzo Pieralisi /* Global IRQs */ 1389d6fcd3b1SLorenzo Pieralisi hw_irq = IORT_IRQ_MASK(glb_irq[0]); 1390d6fcd3b1SLorenzo Pieralisi trigger = IORT_IRQ_TRIGGER_MASK(glb_irq[0]); 1391d6fcd3b1SLorenzo Pieralisi 1392d6fcd3b1SLorenzo Pieralisi acpi_iort_register_irq(hw_irq, "arm-smmu-global", trigger, 1393d6fcd3b1SLorenzo Pieralisi &res[num_res++]); 1394d6fcd3b1SLorenzo Pieralisi 1395d6fcd3b1SLorenzo Pieralisi /* Context IRQs */ 1396d6fcd3b1SLorenzo Pieralisi ctx_irq = ACPI_ADD_PTR(u64, node, smmu->context_interrupt_offset); 1397d6fcd3b1SLorenzo Pieralisi for (i = 0; i < smmu->context_interrupt_count; i++) { 1398d6fcd3b1SLorenzo Pieralisi hw_irq = IORT_IRQ_MASK(ctx_irq[i]); 1399d6fcd3b1SLorenzo Pieralisi trigger = IORT_IRQ_TRIGGER_MASK(ctx_irq[i]); 1400d6fcd3b1SLorenzo Pieralisi 1401d6fcd3b1SLorenzo Pieralisi acpi_iort_register_irq(hw_irq, "arm-smmu-context", trigger, 1402d6fcd3b1SLorenzo Pieralisi &res[num_res++]); 1403d6fcd3b1SLorenzo Pieralisi } 1404d6fcd3b1SLorenzo Pieralisi } 1405d6fcd3b1SLorenzo Pieralisi 140624e51604SNeil Leeder static void __init arm_smmu_dma_configure(struct device *dev, 140724e51604SNeil Leeder struct acpi_iort_node *node) 1408d6fcd3b1SLorenzo Pieralisi { 1409d6fcd3b1SLorenzo Pieralisi struct acpi_iort_smmu *smmu; 141024e51604SNeil Leeder enum dev_dma_attr attr; 1411d6fcd3b1SLorenzo Pieralisi 1412d6fcd3b1SLorenzo Pieralisi /* Retrieve SMMU specific data */ 1413d6fcd3b1SLorenzo Pieralisi smmu = (struct acpi_iort_smmu *)node->node_data; 1414d6fcd3b1SLorenzo Pieralisi 141524e51604SNeil Leeder attr = (smmu->flags & ACPI_IORT_SMMU_COHERENT_WALK) ? 141624e51604SNeil Leeder DEV_DMA_COHERENT : DEV_DMA_NON_COHERENT; 141724e51604SNeil Leeder 141824e51604SNeil Leeder /* We expect the dma masks to be equivalent for SMMU set-ups */ 141924e51604SNeil Leeder dev->dma_mask = &dev->coherent_dma_mask; 142024e51604SNeil Leeder 142124e51604SNeil Leeder /* Configure DMA for the page table walker */ 142224e51604SNeil Leeder acpi_dma_configure(dev, attr); 142324e51604SNeil Leeder } 142424e51604SNeil Leeder 142524e51604SNeil Leeder static int __init arm_smmu_v3_pmcg_count_resources(struct acpi_iort_node *node) 142624e51604SNeil Leeder { 142724e51604SNeil Leeder struct acpi_iort_pmcg *pmcg; 142824e51604SNeil Leeder 142924e51604SNeil Leeder /* Retrieve PMCG specific data */ 143024e51604SNeil Leeder pmcg = (struct acpi_iort_pmcg *)node->node_data; 143124e51604SNeil Leeder 143224e51604SNeil Leeder /* 143324e51604SNeil Leeder * There are always 2 memory resources. 143424e51604SNeil Leeder * If the overflow_gsiv is present then add that for a total of 3. 143524e51604SNeil Leeder */ 143624e51604SNeil Leeder return pmcg->overflow_gsiv ? 3 : 2; 143724e51604SNeil Leeder } 143824e51604SNeil Leeder 143924e51604SNeil Leeder static void __init arm_smmu_v3_pmcg_init_resources(struct resource *res, 144024e51604SNeil Leeder struct acpi_iort_node *node) 144124e51604SNeil Leeder { 144224e51604SNeil Leeder struct acpi_iort_pmcg *pmcg; 144324e51604SNeil Leeder 144424e51604SNeil Leeder /* Retrieve PMCG specific data */ 144524e51604SNeil Leeder pmcg = (struct acpi_iort_pmcg *)node->node_data; 144624e51604SNeil Leeder 144724e51604SNeil Leeder res[0].start = pmcg->page0_base_address; 144824e51604SNeil Leeder res[0].end = pmcg->page0_base_address + SZ_4K - 1; 144924e51604SNeil Leeder res[0].flags = IORESOURCE_MEM; 145024e51604SNeil Leeder res[1].start = pmcg->page1_base_address; 145124e51604SNeil Leeder res[1].end = pmcg->page1_base_address + SZ_4K - 1; 145224e51604SNeil Leeder res[1].flags = IORESOURCE_MEM; 145324e51604SNeil Leeder 145424e51604SNeil Leeder if (pmcg->overflow_gsiv) 145524e51604SNeil Leeder acpi_iort_register_irq(pmcg->overflow_gsiv, "overflow", 145624e51604SNeil Leeder ACPI_EDGE_SENSITIVE, &res[2]); 145724e51604SNeil Leeder } 145824e51604SNeil Leeder 145924062fe8SShameer Kolothum static struct acpi_platform_list pmcg_plat_info[] __initdata = { 146024062fe8SShameer Kolothum /* HiSilicon Hip08 Platform */ 146124062fe8SShameer Kolothum {"HISI ", "HIP08 ", 0, ACPI_SIG_IORT, greater_than_or_equal, 146224062fe8SShameer Kolothum "Erratum #162001800", IORT_SMMU_V3_PMCG_HISI_HIP08}, 146324062fe8SShameer Kolothum { } 146424062fe8SShameer Kolothum }; 146524062fe8SShameer Kolothum 146624e51604SNeil Leeder static int __init arm_smmu_v3_pmcg_add_platdata(struct platform_device *pdev) 146724e51604SNeil Leeder { 146824062fe8SShameer Kolothum u32 model; 146924062fe8SShameer Kolothum int idx; 147024062fe8SShameer Kolothum 147124062fe8SShameer Kolothum idx = acpi_match_platform_list(pmcg_plat_info); 147224062fe8SShameer Kolothum if (idx >= 0) 147324062fe8SShameer Kolothum model = pmcg_plat_info[idx].data; 147424062fe8SShameer Kolothum else 147524062fe8SShameer Kolothum model = IORT_SMMU_V3_PMCG_GENERIC; 147624e51604SNeil Leeder 147724e51604SNeil Leeder return platform_device_add_data(pdev, &model, sizeof(model)); 1478d6fcd3b1SLorenzo Pieralisi } 1479d6fcd3b1SLorenzo Pieralisi 1480896dd2c3SLorenzo Pieralisi struct iort_dev_config { 1481846f0e9eSLorenzo Pieralisi const char *name; 1482896dd2c3SLorenzo Pieralisi int (*dev_init)(struct acpi_iort_node *node); 148324e51604SNeil Leeder void (*dev_dma_configure)(struct device *dev, 148424e51604SNeil Leeder struct acpi_iort_node *node); 1485896dd2c3SLorenzo Pieralisi int (*dev_count_resources)(struct acpi_iort_node *node); 1486896dd2c3SLorenzo Pieralisi void (*dev_init_resources)(struct resource *res, 1487846f0e9eSLorenzo Pieralisi struct acpi_iort_node *node); 148836a2ba07SKefeng Wang int (*dev_set_proximity)(struct device *dev, 14895fe0ce3bSGanapatrao Kulkarni struct acpi_iort_node *node); 149024e51604SNeil Leeder int (*dev_add_platdata)(struct platform_device *pdev); 1491846f0e9eSLorenzo Pieralisi }; 1492846f0e9eSLorenzo Pieralisi 1493896dd2c3SLorenzo Pieralisi static const struct iort_dev_config iort_arm_smmu_v3_cfg __initconst = { 1494e4dadfa8SLorenzo Pieralisi .name = "arm-smmu-v3", 149524e51604SNeil Leeder .dev_dma_configure = arm_smmu_v3_dma_configure, 1496896dd2c3SLorenzo Pieralisi .dev_count_resources = arm_smmu_v3_count_resources, 1497896dd2c3SLorenzo Pieralisi .dev_init_resources = arm_smmu_v3_init_resources, 1498896dd2c3SLorenzo Pieralisi .dev_set_proximity = arm_smmu_v3_set_proximity, 1499e4dadfa8SLorenzo Pieralisi }; 1500e4dadfa8SLorenzo Pieralisi 1501896dd2c3SLorenzo Pieralisi static const struct iort_dev_config iort_arm_smmu_cfg __initconst = { 1502d6fcd3b1SLorenzo Pieralisi .name = "arm-smmu", 150324e51604SNeil Leeder .dev_dma_configure = arm_smmu_dma_configure, 1504896dd2c3SLorenzo Pieralisi .dev_count_resources = arm_smmu_count_resources, 150524e51604SNeil Leeder .dev_init_resources = arm_smmu_init_resources, 150624e51604SNeil Leeder }; 150724e51604SNeil Leeder 150824e51604SNeil Leeder static const struct iort_dev_config iort_arm_smmu_v3_pmcg_cfg __initconst = { 150924e51604SNeil Leeder .name = "arm-smmu-v3-pmcg", 151024e51604SNeil Leeder .dev_count_resources = arm_smmu_v3_pmcg_count_resources, 151124e51604SNeil Leeder .dev_init_resources = arm_smmu_v3_pmcg_init_resources, 151224e51604SNeil Leeder .dev_add_platdata = arm_smmu_v3_pmcg_add_platdata, 1513d6fcd3b1SLorenzo Pieralisi }; 1514d6fcd3b1SLorenzo Pieralisi 1515896dd2c3SLorenzo Pieralisi static __init const struct iort_dev_config *iort_get_dev_cfg( 1516e3d49392SLorenzo Pieralisi struct acpi_iort_node *node) 1517846f0e9eSLorenzo Pieralisi { 1518e4dadfa8SLorenzo Pieralisi switch (node->type) { 1519e4dadfa8SLorenzo Pieralisi case ACPI_IORT_NODE_SMMU_V3: 1520e4dadfa8SLorenzo Pieralisi return &iort_arm_smmu_v3_cfg; 1521d6fcd3b1SLorenzo Pieralisi case ACPI_IORT_NODE_SMMU: 1522d6fcd3b1SLorenzo Pieralisi return &iort_arm_smmu_cfg; 152324e51604SNeil Leeder case ACPI_IORT_NODE_PMCG: 152424e51604SNeil Leeder return &iort_arm_smmu_v3_pmcg_cfg; 1525e4dadfa8SLorenzo Pieralisi default: 1526846f0e9eSLorenzo Pieralisi return NULL; 1527846f0e9eSLorenzo Pieralisi } 1528e4dadfa8SLorenzo Pieralisi } 1529846f0e9eSLorenzo Pieralisi 1530846f0e9eSLorenzo Pieralisi /** 1531896dd2c3SLorenzo Pieralisi * iort_add_platform_device() - Allocate a platform device for IORT node 1532896dd2c3SLorenzo Pieralisi * @node: Pointer to device ACPI IORT node 1533846f0e9eSLorenzo Pieralisi * 1534846f0e9eSLorenzo Pieralisi * Returns: 0 on success, <0 failure 1535846f0e9eSLorenzo Pieralisi */ 1536896dd2c3SLorenzo Pieralisi static int __init iort_add_platform_device(struct acpi_iort_node *node, 1537896dd2c3SLorenzo Pieralisi const struct iort_dev_config *ops) 1538846f0e9eSLorenzo Pieralisi { 1539846f0e9eSLorenzo Pieralisi struct fwnode_handle *fwnode; 1540846f0e9eSLorenzo Pieralisi struct platform_device *pdev; 1541846f0e9eSLorenzo Pieralisi struct resource *r; 1542846f0e9eSLorenzo Pieralisi int ret, count; 1543846f0e9eSLorenzo Pieralisi 1544846f0e9eSLorenzo Pieralisi pdev = platform_device_alloc(ops->name, PLATFORM_DEVID_AUTO); 1545846f0e9eSLorenzo Pieralisi if (!pdev) 15465e5afa6cSDan Carpenter return -ENOMEM; 1547846f0e9eSLorenzo Pieralisi 154836a2ba07SKefeng Wang if (ops->dev_set_proximity) { 154936a2ba07SKefeng Wang ret = ops->dev_set_proximity(&pdev->dev, node); 155036a2ba07SKefeng Wang if (ret) 155136a2ba07SKefeng Wang goto dev_put; 155236a2ba07SKefeng Wang } 15535fe0ce3bSGanapatrao Kulkarni 1554896dd2c3SLorenzo Pieralisi count = ops->dev_count_resources(node); 1555846f0e9eSLorenzo Pieralisi 1556846f0e9eSLorenzo Pieralisi r = kcalloc(count, sizeof(*r), GFP_KERNEL); 1557846f0e9eSLorenzo Pieralisi if (!r) { 1558846f0e9eSLorenzo Pieralisi ret = -ENOMEM; 1559846f0e9eSLorenzo Pieralisi goto dev_put; 1560846f0e9eSLorenzo Pieralisi } 1561846f0e9eSLorenzo Pieralisi 1562896dd2c3SLorenzo Pieralisi ops->dev_init_resources(r, node); 1563846f0e9eSLorenzo Pieralisi 1564846f0e9eSLorenzo Pieralisi ret = platform_device_add_resources(pdev, r, count); 1565846f0e9eSLorenzo Pieralisi /* 1566846f0e9eSLorenzo Pieralisi * Resources are duplicated in platform_device_add_resources, 1567846f0e9eSLorenzo Pieralisi * free their allocated memory 1568846f0e9eSLorenzo Pieralisi */ 1569846f0e9eSLorenzo Pieralisi kfree(r); 1570846f0e9eSLorenzo Pieralisi 1571846f0e9eSLorenzo Pieralisi if (ret) 1572846f0e9eSLorenzo Pieralisi goto dev_put; 1573846f0e9eSLorenzo Pieralisi 1574846f0e9eSLorenzo Pieralisi /* 157524e51604SNeil Leeder * Platform devices based on PMCG nodes uses platform_data to 157624e51604SNeil Leeder * pass the hardware model info to the driver. For others, add 157724e51604SNeil Leeder * a copy of IORT node pointer to platform_data to be used to 157824e51604SNeil Leeder * retrieve IORT data information. 1579846f0e9eSLorenzo Pieralisi */ 158024e51604SNeil Leeder if (ops->dev_add_platdata) 158124e51604SNeil Leeder ret = ops->dev_add_platdata(pdev); 158224e51604SNeil Leeder else 1583846f0e9eSLorenzo Pieralisi ret = platform_device_add_data(pdev, &node, sizeof(node)); 158424e51604SNeil Leeder 1585846f0e9eSLorenzo Pieralisi if (ret) 1586846f0e9eSLorenzo Pieralisi goto dev_put; 1587846f0e9eSLorenzo Pieralisi 1588846f0e9eSLorenzo Pieralisi fwnode = iort_get_fwnode(node); 1589846f0e9eSLorenzo Pieralisi 1590846f0e9eSLorenzo Pieralisi if (!fwnode) { 1591846f0e9eSLorenzo Pieralisi ret = -ENODEV; 1592846f0e9eSLorenzo Pieralisi goto dev_put; 1593846f0e9eSLorenzo Pieralisi } 1594846f0e9eSLorenzo Pieralisi 1595846f0e9eSLorenzo Pieralisi pdev->dev.fwnode = fwnode; 1596846f0e9eSLorenzo Pieralisi 159724e51604SNeil Leeder if (ops->dev_dma_configure) 159824e51604SNeil Leeder ops->dev_dma_configure(&pdev->dev, node); 1599846f0e9eSLorenzo Pieralisi 160065637901SLorenzo Pieralisi iort_set_device_domain(&pdev->dev, node); 160165637901SLorenzo Pieralisi 1602846f0e9eSLorenzo Pieralisi ret = platform_device_add(pdev); 1603846f0e9eSLorenzo Pieralisi if (ret) 1604846f0e9eSLorenzo Pieralisi goto dma_deconfigure; 1605846f0e9eSLorenzo Pieralisi 1606846f0e9eSLorenzo Pieralisi return 0; 1607846f0e9eSLorenzo Pieralisi 1608846f0e9eSLorenzo Pieralisi dma_deconfigure: 1609dc3c0550SChristoph Hellwig arch_teardown_dma_ops(&pdev->dev); 1610846f0e9eSLorenzo Pieralisi dev_put: 1611846f0e9eSLorenzo Pieralisi platform_device_put(pdev); 1612846f0e9eSLorenzo Pieralisi 1613846f0e9eSLorenzo Pieralisi return ret; 1614846f0e9eSLorenzo Pieralisi } 1615846f0e9eSLorenzo Pieralisi 161643554cebSSinan Kaya #ifdef CONFIG_PCI 161743554cebSSinan Kaya static void __init iort_enable_acs(struct acpi_iort_node *iort_node) 161837f6b42eSLorenzo Pieralisi { 161943554cebSSinan Kaya static bool acs_enabled __initdata; 162043554cebSSinan Kaya 162143554cebSSinan Kaya if (acs_enabled) 162243554cebSSinan Kaya return; 162343554cebSSinan Kaya 162437f6b42eSLorenzo Pieralisi if (iort_node->type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX) { 162537f6b42eSLorenzo Pieralisi struct acpi_iort_node *parent; 162637f6b42eSLorenzo Pieralisi struct acpi_iort_id_mapping *map; 162737f6b42eSLorenzo Pieralisi int i; 162837f6b42eSLorenzo Pieralisi 162937f6b42eSLorenzo Pieralisi map = ACPI_ADD_PTR(struct acpi_iort_id_mapping, iort_node, 163037f6b42eSLorenzo Pieralisi iort_node->mapping_offset); 163137f6b42eSLorenzo Pieralisi 163237f6b42eSLorenzo Pieralisi for (i = 0; i < iort_node->mapping_count; i++, map++) { 163337f6b42eSLorenzo Pieralisi if (!map->output_reference) 163437f6b42eSLorenzo Pieralisi continue; 163537f6b42eSLorenzo Pieralisi 163637f6b42eSLorenzo Pieralisi parent = ACPI_ADD_PTR(struct acpi_iort_node, 163737f6b42eSLorenzo Pieralisi iort_table, map->output_reference); 163837f6b42eSLorenzo Pieralisi /* 163937f6b42eSLorenzo Pieralisi * If we detect a RC->SMMU mapping, make sure 164037f6b42eSLorenzo Pieralisi * we enable ACS on the system. 164137f6b42eSLorenzo Pieralisi */ 164237f6b42eSLorenzo Pieralisi if ((parent->type == ACPI_IORT_NODE_SMMU) || 164337f6b42eSLorenzo Pieralisi (parent->type == ACPI_IORT_NODE_SMMU_V3)) { 164437f6b42eSLorenzo Pieralisi pci_request_acs(); 164543554cebSSinan Kaya acs_enabled = true; 164643554cebSSinan Kaya return; 164737f6b42eSLorenzo Pieralisi } 164837f6b42eSLorenzo Pieralisi } 164937f6b42eSLorenzo Pieralisi } 165037f6b42eSLorenzo Pieralisi } 165143554cebSSinan Kaya #else 165243554cebSSinan Kaya static inline void iort_enable_acs(struct acpi_iort_node *iort_node) { } 165343554cebSSinan Kaya #endif 165437f6b42eSLorenzo Pieralisi 1655846f0e9eSLorenzo Pieralisi static void __init iort_init_platform_devices(void) 1656846f0e9eSLorenzo Pieralisi { 1657846f0e9eSLorenzo Pieralisi struct acpi_iort_node *iort_node, *iort_end; 1658846f0e9eSLorenzo Pieralisi struct acpi_table_iort *iort; 1659846f0e9eSLorenzo Pieralisi struct fwnode_handle *fwnode; 1660846f0e9eSLorenzo Pieralisi int i, ret; 1661896dd2c3SLorenzo Pieralisi const struct iort_dev_config *ops; 1662846f0e9eSLorenzo Pieralisi 1663846f0e9eSLorenzo Pieralisi /* 1664846f0e9eSLorenzo Pieralisi * iort_table and iort both point to the start of IORT table, but 1665846f0e9eSLorenzo Pieralisi * have different struct types 1666846f0e9eSLorenzo Pieralisi */ 1667846f0e9eSLorenzo Pieralisi iort = (struct acpi_table_iort *)iort_table; 1668846f0e9eSLorenzo Pieralisi 1669846f0e9eSLorenzo Pieralisi /* Get the first IORT node */ 1670846f0e9eSLorenzo Pieralisi iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort, 1671846f0e9eSLorenzo Pieralisi iort->node_offset); 1672846f0e9eSLorenzo Pieralisi iort_end = ACPI_ADD_PTR(struct acpi_iort_node, iort, 1673846f0e9eSLorenzo Pieralisi iort_table->length); 1674846f0e9eSLorenzo Pieralisi 1675846f0e9eSLorenzo Pieralisi for (i = 0; i < iort->node_count; i++) { 1676846f0e9eSLorenzo Pieralisi if (iort_node >= iort_end) { 1677846f0e9eSLorenzo Pieralisi pr_err("iort node pointer overflows, bad table\n"); 1678846f0e9eSLorenzo Pieralisi return; 1679846f0e9eSLorenzo Pieralisi } 1680846f0e9eSLorenzo Pieralisi 168143554cebSSinan Kaya iort_enable_acs(iort_node); 168237f6b42eSLorenzo Pieralisi 1683896dd2c3SLorenzo Pieralisi ops = iort_get_dev_cfg(iort_node); 1684896dd2c3SLorenzo Pieralisi if (ops) { 1685846f0e9eSLorenzo Pieralisi fwnode = acpi_alloc_fwnode_static(); 1686846f0e9eSLorenzo Pieralisi if (!fwnode) 1687846f0e9eSLorenzo Pieralisi return; 1688846f0e9eSLorenzo Pieralisi 1689846f0e9eSLorenzo Pieralisi iort_set_fwnode(iort_node, fwnode); 1690846f0e9eSLorenzo Pieralisi 1691896dd2c3SLorenzo Pieralisi ret = iort_add_platform_device(iort_node, ops); 1692846f0e9eSLorenzo Pieralisi if (ret) { 1693846f0e9eSLorenzo Pieralisi iort_delete_fwnode(iort_node); 1694846f0e9eSLorenzo Pieralisi acpi_free_fwnode_static(fwnode); 1695846f0e9eSLorenzo Pieralisi return; 1696846f0e9eSLorenzo Pieralisi } 1697846f0e9eSLorenzo Pieralisi } 1698846f0e9eSLorenzo Pieralisi 1699846f0e9eSLorenzo Pieralisi iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort_node, 1700846f0e9eSLorenzo Pieralisi iort_node->length); 1701846f0e9eSLorenzo Pieralisi } 1702846f0e9eSLorenzo Pieralisi } 1703846f0e9eSLorenzo Pieralisi 170488ef16d8STomasz Nowicki void __init acpi_iort_init(void) 170588ef16d8STomasz Nowicki { 170688ef16d8STomasz Nowicki acpi_status status; 170788ef16d8STomasz Nowicki 1708701dafe0SHanjun Guo /* iort_table will be used at runtime after the iort init, 1709701dafe0SHanjun Guo * so we don't need to call acpi_put_table() to release 1710701dafe0SHanjun Guo * the IORT table mapping. 1711701dafe0SHanjun Guo */ 171288ef16d8STomasz Nowicki status = acpi_get_table(ACPI_SIG_IORT, 0, &iort_table); 171334ceea27SLorenzo Pieralisi if (ACPI_FAILURE(status)) { 171434ceea27SLorenzo Pieralisi if (status != AE_NOT_FOUND) { 171588ef16d8STomasz Nowicki const char *msg = acpi_format_exception(status); 171634ceea27SLorenzo Pieralisi 171788ef16d8STomasz Nowicki pr_err("Failed to get table, %s\n", msg); 171888ef16d8STomasz Nowicki } 171934ceea27SLorenzo Pieralisi 172034ceea27SLorenzo Pieralisi return; 172134ceea27SLorenzo Pieralisi } 172234ceea27SLorenzo Pieralisi 1723846f0e9eSLorenzo Pieralisi iort_init_platform_devices(); 172488ef16d8STomasz Nowicki } 1725