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 }; 26788ef16d8STomasz Nowicki struct acpi_device *adev = to_acpi_device_node(dev->fwnode); 26888ef16d8STomasz Nowicki struct acpi_iort_named_component *ncomp; 26988ef16d8STomasz Nowicki 270c92bdfe8SHanjun Guo if (!adev) 27188ef16d8STomasz Nowicki goto out; 27288ef16d8STomasz Nowicki 27388ef16d8STomasz Nowicki status = acpi_get_name(adev->handle, ACPI_FULL_PATHNAME, &buf); 27488ef16d8STomasz Nowicki if (ACPI_FAILURE(status)) { 27588ef16d8STomasz Nowicki dev_warn(dev, "Can't get device full path name\n"); 27688ef16d8STomasz Nowicki goto out; 27788ef16d8STomasz Nowicki } 27888ef16d8STomasz Nowicki 27988ef16d8STomasz Nowicki ncomp = (struct acpi_iort_named_component *)node->node_data; 28088ef16d8STomasz Nowicki status = !strcmp(ncomp->device_name, buf.pointer) ? 28188ef16d8STomasz Nowicki AE_OK : AE_NOT_FOUND; 28288ef16d8STomasz Nowicki acpi_os_free(buf.pointer); 28388ef16d8STomasz Nowicki } else if (node->type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX) { 28488ef16d8STomasz Nowicki struct acpi_iort_root_complex *pci_rc; 28588ef16d8STomasz Nowicki struct pci_bus *bus; 28688ef16d8STomasz Nowicki 28788ef16d8STomasz Nowicki bus = to_pci_bus(dev); 28888ef16d8STomasz Nowicki pci_rc = (struct acpi_iort_root_complex *)node->node_data; 28988ef16d8STomasz Nowicki 29088ef16d8STomasz Nowicki /* 29188ef16d8STomasz Nowicki * It is assumed that PCI segment numbers maps one-to-one 29288ef16d8STomasz Nowicki * with root complexes. Each segment number can represent only 29388ef16d8STomasz Nowicki * one root complex. 29488ef16d8STomasz Nowicki */ 29588ef16d8STomasz Nowicki status = pci_rc->pci_segment_number == pci_domain_nr(bus) ? 29688ef16d8STomasz Nowicki AE_OK : AE_NOT_FOUND; 29788ef16d8STomasz Nowicki } 29888ef16d8STomasz Nowicki out: 29988ef16d8STomasz Nowicki return status; 30088ef16d8STomasz Nowicki } 30188ef16d8STomasz Nowicki 30288ef16d8STomasz Nowicki static int iort_id_map(struct acpi_iort_id_mapping *map, u8 type, u32 rid_in, 30388ef16d8STomasz Nowicki u32 *rid_out) 30488ef16d8STomasz Nowicki { 30588ef16d8STomasz Nowicki /* Single mapping does not care for input id */ 30688ef16d8STomasz Nowicki if (map->flags & ACPI_IORT_ID_SINGLE_MAPPING) { 30788ef16d8STomasz Nowicki if (type == ACPI_IORT_NODE_NAMED_COMPONENT || 30888ef16d8STomasz Nowicki type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX) { 30988ef16d8STomasz Nowicki *rid_out = map->output_base; 31088ef16d8STomasz Nowicki return 0; 31188ef16d8STomasz Nowicki } 31288ef16d8STomasz Nowicki 31388ef16d8STomasz Nowicki pr_warn(FW_BUG "[map %p] SINGLE MAPPING flag not allowed for node type %d, skipping ID map\n", 31488ef16d8STomasz Nowicki map, type); 31588ef16d8STomasz Nowicki return -ENXIO; 31688ef16d8STomasz Nowicki } 31788ef16d8STomasz Nowicki 318*6d3b29d0SArd Biesheuvel if (rid_in < map->input_base || 319*6d3b29d0SArd Biesheuvel (rid_in >= map->input_base + map->id_count)) 32088ef16d8STomasz Nowicki return -ENXIO; 32188ef16d8STomasz Nowicki 32288ef16d8STomasz Nowicki *rid_out = map->output_base + (rid_in - map->input_base); 32388ef16d8STomasz Nowicki return 0; 32488ef16d8STomasz Nowicki } 32588ef16d8STomasz Nowicki 326e3d49392SLorenzo Pieralisi static struct acpi_iort_node *iort_node_get_id(struct acpi_iort_node *node, 3278ca4f1d3SHanjun Guo u32 *id_out, int index) 328618f535aSLorenzo Pieralisi { 329618f535aSLorenzo Pieralisi struct acpi_iort_node *parent; 330618f535aSLorenzo Pieralisi struct acpi_iort_id_mapping *map; 331618f535aSLorenzo Pieralisi 332618f535aSLorenzo Pieralisi if (!node->mapping_offset || !node->mapping_count || 333618f535aSLorenzo Pieralisi index >= node->mapping_count) 334618f535aSLorenzo Pieralisi return NULL; 335618f535aSLorenzo Pieralisi 336618f535aSLorenzo Pieralisi map = ACPI_ADD_PTR(struct acpi_iort_id_mapping, node, 337030abd8aSLorenzo Pieralisi node->mapping_offset + index * sizeof(*map)); 338618f535aSLorenzo Pieralisi 339618f535aSLorenzo Pieralisi /* Firmware bug! */ 340618f535aSLorenzo Pieralisi if (!map->output_reference) { 341618f535aSLorenzo Pieralisi pr_err(FW_BUG "[node %p type %d] ID map has NULL parent reference\n", 342618f535aSLorenzo Pieralisi node, node->type); 343618f535aSLorenzo Pieralisi return NULL; 344618f535aSLorenzo Pieralisi } 345618f535aSLorenzo Pieralisi 346618f535aSLorenzo Pieralisi parent = ACPI_ADD_PTR(struct acpi_iort_node, iort_table, 347618f535aSLorenzo Pieralisi map->output_reference); 348618f535aSLorenzo Pieralisi 349030abd8aSLorenzo Pieralisi if (map->flags & ACPI_IORT_ID_SINGLE_MAPPING) { 350618f535aSLorenzo Pieralisi if (node->type == ACPI_IORT_NODE_NAMED_COMPONENT || 35186456a3fSHanjun Guo node->type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX || 35224e51604SNeil Leeder node->type == ACPI_IORT_NODE_SMMU_V3 || 35324e51604SNeil Leeder node->type == ACPI_IORT_NODE_PMCG) { 354030abd8aSLorenzo Pieralisi *id_out = map->output_base; 355618f535aSLorenzo Pieralisi return parent; 356618f535aSLorenzo Pieralisi } 357618f535aSLorenzo Pieralisi } 358618f535aSLorenzo Pieralisi 359618f535aSLorenzo Pieralisi return NULL; 360618f535aSLorenzo Pieralisi } 361618f535aSLorenzo Pieralisi 36286456a3fSHanjun Guo static int iort_get_id_mapping_index(struct acpi_iort_node *node) 36386456a3fSHanjun Guo { 36486456a3fSHanjun Guo struct acpi_iort_smmu_v3 *smmu; 36586456a3fSHanjun Guo 36686456a3fSHanjun Guo switch (node->type) { 36786456a3fSHanjun Guo case ACPI_IORT_NODE_SMMU_V3: 36886456a3fSHanjun Guo /* 36986456a3fSHanjun Guo * SMMUv3 dev ID mapping index was introduced in revision 1 37086456a3fSHanjun Guo * table, not available in revision 0 37186456a3fSHanjun Guo */ 37286456a3fSHanjun Guo if (node->revision < 1) 37386456a3fSHanjun Guo return -EINVAL; 37486456a3fSHanjun Guo 37586456a3fSHanjun Guo smmu = (struct acpi_iort_smmu_v3 *)node->node_data; 37686456a3fSHanjun Guo /* 37786456a3fSHanjun Guo * ID mapping index is only ignored if all interrupts are 37886456a3fSHanjun Guo * GSIV based 37986456a3fSHanjun Guo */ 38086456a3fSHanjun Guo if (smmu->event_gsiv && smmu->pri_gsiv && smmu->gerr_gsiv 38186456a3fSHanjun Guo && smmu->sync_gsiv) 38286456a3fSHanjun Guo return -EINVAL; 38386456a3fSHanjun Guo 38486456a3fSHanjun Guo if (smmu->id_mapping_index >= node->mapping_count) { 38586456a3fSHanjun Guo pr_err(FW_BUG "[node %p type %d] ID mapping index overflows valid mappings\n", 38686456a3fSHanjun Guo node, node->type); 38786456a3fSHanjun Guo return -EINVAL; 38886456a3fSHanjun Guo } 38986456a3fSHanjun Guo 39086456a3fSHanjun Guo return smmu->id_mapping_index; 39124e51604SNeil Leeder case ACPI_IORT_NODE_PMCG: 39224e51604SNeil Leeder return 0; 39386456a3fSHanjun Guo default: 39486456a3fSHanjun Guo return -EINVAL; 39586456a3fSHanjun Guo } 39686456a3fSHanjun Guo } 3978c8df8dcSHanjun Guo 398697f6093SHanjun Guo static struct acpi_iort_node *iort_node_map_id(struct acpi_iort_node *node, 399697f6093SHanjun Guo u32 id_in, u32 *id_out, 400ea50b524SLorenzo Pieralisi u8 type_mask) 40188ef16d8STomasz Nowicki { 402697f6093SHanjun Guo u32 id = id_in; 40388ef16d8STomasz Nowicki 40488ef16d8STomasz Nowicki /* Parse the ID mapping tree to find specified node type */ 40588ef16d8STomasz Nowicki while (node) { 40688ef16d8STomasz Nowicki struct acpi_iort_id_mapping *map; 4078c8df8dcSHanjun Guo int i, index; 40888ef16d8STomasz Nowicki 409ea50b524SLorenzo Pieralisi if (IORT_TYPE_MASK(node->type) & type_mask) { 410697f6093SHanjun Guo if (id_out) 411697f6093SHanjun Guo *id_out = id; 41288ef16d8STomasz Nowicki return node; 41388ef16d8STomasz Nowicki } 41488ef16d8STomasz Nowicki 41588ef16d8STomasz Nowicki if (!node->mapping_offset || !node->mapping_count) 41688ef16d8STomasz Nowicki goto fail_map; 41788ef16d8STomasz Nowicki 41888ef16d8STomasz Nowicki map = ACPI_ADD_PTR(struct acpi_iort_id_mapping, node, 41988ef16d8STomasz Nowicki node->mapping_offset); 42088ef16d8STomasz Nowicki 42188ef16d8STomasz Nowicki /* Firmware bug! */ 42288ef16d8STomasz Nowicki if (!map->output_reference) { 42388ef16d8STomasz Nowicki pr_err(FW_BUG "[node %p type %d] ID map has NULL parent reference\n", 42488ef16d8STomasz Nowicki node, node->type); 42588ef16d8STomasz Nowicki goto fail_map; 42688ef16d8STomasz Nowicki } 42788ef16d8STomasz Nowicki 4288c8df8dcSHanjun Guo /* 4298c8df8dcSHanjun Guo * Get the special ID mapping index (if any) and skip its 4308c8df8dcSHanjun Guo * associated ID map to prevent erroneous multi-stage 4318c8df8dcSHanjun Guo * IORT ID translations. 4328c8df8dcSHanjun Guo */ 4338c8df8dcSHanjun Guo index = iort_get_id_mapping_index(node); 4348c8df8dcSHanjun Guo 435697f6093SHanjun Guo /* Do the ID translation */ 43688ef16d8STomasz Nowicki for (i = 0; i < node->mapping_count; i++, map++) { 4378c8df8dcSHanjun Guo /* if it is special mapping index, skip it */ 4388c8df8dcSHanjun Guo if (i == index) 4398c8df8dcSHanjun Guo continue; 4408c8df8dcSHanjun Guo 441697f6093SHanjun Guo if (!iort_id_map(map, node->type, id, &id)) 44288ef16d8STomasz Nowicki break; 44388ef16d8STomasz Nowicki } 44488ef16d8STomasz Nowicki 44588ef16d8STomasz Nowicki if (i == node->mapping_count) 44688ef16d8STomasz Nowicki goto fail_map; 44788ef16d8STomasz Nowicki 44888ef16d8STomasz Nowicki node = ACPI_ADD_PTR(struct acpi_iort_node, iort_table, 44988ef16d8STomasz Nowicki map->output_reference); 45088ef16d8STomasz Nowicki } 45188ef16d8STomasz Nowicki 45288ef16d8STomasz Nowicki fail_map: 453697f6093SHanjun Guo /* Map input ID to output ID unchanged on mapping failure */ 454697f6093SHanjun Guo if (id_out) 455697f6093SHanjun Guo *id_out = id_in; 45688ef16d8STomasz Nowicki 45788ef16d8STomasz Nowicki return NULL; 45888ef16d8STomasz Nowicki } 45988ef16d8STomasz Nowicki 460e3d49392SLorenzo Pieralisi static struct acpi_iort_node *iort_node_map_platform_id( 461e3d49392SLorenzo Pieralisi struct acpi_iort_node *node, u32 *id_out, u8 type_mask, 4628ca4f1d3SHanjun Guo int index) 4638ca4f1d3SHanjun Guo { 4648ca4f1d3SHanjun Guo struct acpi_iort_node *parent; 4658ca4f1d3SHanjun Guo u32 id; 4668ca4f1d3SHanjun Guo 4678ca4f1d3SHanjun Guo /* step 1: retrieve the initial dev id */ 4688ca4f1d3SHanjun Guo parent = iort_node_get_id(node, &id, index); 4698ca4f1d3SHanjun Guo if (!parent) 4708ca4f1d3SHanjun Guo return NULL; 4718ca4f1d3SHanjun Guo 4728ca4f1d3SHanjun Guo /* 4738ca4f1d3SHanjun Guo * optional step 2: map the initial dev id if its parent is not 4748ca4f1d3SHanjun Guo * the target type we want, map it again for the use cases such 4758ca4f1d3SHanjun Guo * as NC (named component) -> SMMU -> ITS. If the type is matched, 4768ca4f1d3SHanjun Guo * return the initial dev id and its parent pointer directly. 4778ca4f1d3SHanjun Guo */ 4788ca4f1d3SHanjun Guo if (!(IORT_TYPE_MASK(parent->type) & type_mask)) 4798ca4f1d3SHanjun Guo parent = iort_node_map_id(parent, id, id_out, type_mask); 4808ca4f1d3SHanjun Guo else 4818ca4f1d3SHanjun Guo if (id_out) 4828ca4f1d3SHanjun Guo *id_out = id; 4838ca4f1d3SHanjun Guo 4848ca4f1d3SHanjun Guo return parent; 4858ca4f1d3SHanjun Guo } 4868ca4f1d3SHanjun Guo 48788ef16d8STomasz Nowicki static struct acpi_iort_node *iort_find_dev_node(struct device *dev) 48888ef16d8STomasz Nowicki { 48988ef16d8STomasz Nowicki struct pci_bus *pbus; 49088ef16d8STomasz Nowicki 4910a71d8b9SHanjun Guo if (!dev_is_pci(dev)) { 4920a71d8b9SHanjun Guo struct acpi_iort_node *node; 4930a71d8b9SHanjun Guo /* 4940a71d8b9SHanjun Guo * scan iort_fwnode_list to see if it's an iort platform 4950a71d8b9SHanjun Guo * device (such as SMMU, PMCG),its iort node already cached 4960a71d8b9SHanjun Guo * and associated with fwnode when iort platform devices 4970a71d8b9SHanjun Guo * were initialized. 4980a71d8b9SHanjun Guo */ 4990a71d8b9SHanjun Guo node = iort_get_iort_node(dev->fwnode); 5000a71d8b9SHanjun Guo if (node) 5010a71d8b9SHanjun Guo return node; 5020a71d8b9SHanjun Guo 5030a71d8b9SHanjun Guo /* 5040a71d8b9SHanjun Guo * if not, then it should be a platform device defined in 5050a71d8b9SHanjun Guo * DSDT/SSDT (with Named Component node in IORT) 5060a71d8b9SHanjun Guo */ 50788ef16d8STomasz Nowicki return iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT, 50888ef16d8STomasz Nowicki iort_match_node_callback, dev); 5090a71d8b9SHanjun Guo } 51088ef16d8STomasz Nowicki 51188ef16d8STomasz Nowicki /* Find a PCI root bus */ 51288ef16d8STomasz Nowicki pbus = to_pci_dev(dev)->bus; 51388ef16d8STomasz Nowicki while (!pci_is_root_bus(pbus)) 51488ef16d8STomasz Nowicki pbus = pbus->parent; 51588ef16d8STomasz Nowicki 51688ef16d8STomasz Nowicki return iort_scan_node(ACPI_IORT_NODE_PCI_ROOT_COMPLEX, 51788ef16d8STomasz Nowicki iort_match_node_callback, &pbus->dev); 51888ef16d8STomasz Nowicki } 51988ef16d8STomasz Nowicki 5204bf2efd2STomasz Nowicki /** 5214bf2efd2STomasz Nowicki * iort_msi_map_rid() - Map a MSI requester ID for a device 5224bf2efd2STomasz Nowicki * @dev: The device for which the mapping is to be done. 5234bf2efd2STomasz Nowicki * @req_id: The device requester ID. 5244bf2efd2STomasz Nowicki * 5254bf2efd2STomasz Nowicki * Returns: mapped MSI RID on success, input requester ID otherwise 5264bf2efd2STomasz Nowicki */ 5274bf2efd2STomasz Nowicki u32 iort_msi_map_rid(struct device *dev, u32 req_id) 5284bf2efd2STomasz Nowicki { 5294bf2efd2STomasz Nowicki struct acpi_iort_node *node; 5304bf2efd2STomasz Nowicki u32 dev_id; 5314bf2efd2STomasz Nowicki 5324bf2efd2STomasz Nowicki node = iort_find_dev_node(dev); 5334bf2efd2STomasz Nowicki if (!node) 5344bf2efd2STomasz Nowicki return req_id; 5354bf2efd2STomasz Nowicki 536697f6093SHanjun Guo iort_node_map_id(node, req_id, &dev_id, IORT_MSI_TYPE); 5374bf2efd2STomasz Nowicki return dev_id; 5384bf2efd2STomasz Nowicki } 5394bf2efd2STomasz Nowicki 5404bf2efd2STomasz Nowicki /** 541ae7c1838SHanjun Guo * iort_pmsi_get_dev_id() - Get the device id for a device 542ae7c1838SHanjun Guo * @dev: The device for which the mapping is to be done. 543ae7c1838SHanjun Guo * @dev_id: The device ID found. 544ae7c1838SHanjun Guo * 545ae7c1838SHanjun Guo * Returns: 0 for successful find a dev id, -ENODEV on error 546ae7c1838SHanjun Guo */ 547ae7c1838SHanjun Guo int iort_pmsi_get_dev_id(struct device *dev, u32 *dev_id) 548ae7c1838SHanjun Guo { 5498c8df8dcSHanjun Guo int i, index; 550ae7c1838SHanjun Guo struct acpi_iort_node *node; 551ae7c1838SHanjun Guo 552ae7c1838SHanjun Guo node = iort_find_dev_node(dev); 553ae7c1838SHanjun Guo if (!node) 554ae7c1838SHanjun Guo return -ENODEV; 555ae7c1838SHanjun Guo 5568c8df8dcSHanjun Guo index = iort_get_id_mapping_index(node); 5578c8df8dcSHanjun Guo /* if there is a valid index, go get the dev_id directly */ 5588c8df8dcSHanjun Guo if (index >= 0) { 5598c8df8dcSHanjun Guo if (iort_node_get_id(node, dev_id, index)) 560ae7c1838SHanjun Guo return 0; 5618c8df8dcSHanjun Guo } else { 5628c8df8dcSHanjun Guo for (i = 0; i < node->mapping_count; i++) { 5638c8df8dcSHanjun Guo if (iort_node_map_platform_id(node, dev_id, 5648c8df8dcSHanjun Guo IORT_MSI_TYPE, i)) 5658c8df8dcSHanjun Guo return 0; 5668c8df8dcSHanjun Guo } 567ae7c1838SHanjun Guo } 568ae7c1838SHanjun Guo 569ae7c1838SHanjun Guo return -ENODEV; 570ae7c1838SHanjun Guo } 571ae7c1838SHanjun Guo 5728b4282e6SShameer Kolothum static int __maybe_unused iort_find_its_base(u32 its_id, phys_addr_t *base) 5738b4282e6SShameer Kolothum { 5748b4282e6SShameer Kolothum struct iort_its_msi_chip *its_msi_chip; 5758b4282e6SShameer Kolothum int ret = -ENODEV; 5768b4282e6SShameer Kolothum 5778b4282e6SShameer Kolothum spin_lock(&iort_msi_chip_lock); 5788b4282e6SShameer Kolothum list_for_each_entry(its_msi_chip, &iort_msi_chip_list, list) { 5798b4282e6SShameer Kolothum if (its_msi_chip->translation_id == its_id) { 5808b4282e6SShameer Kolothum *base = its_msi_chip->base_addr; 5818b4282e6SShameer Kolothum ret = 0; 5828b4282e6SShameer Kolothum break; 5838b4282e6SShameer Kolothum } 5848b4282e6SShameer Kolothum } 5858b4282e6SShameer Kolothum spin_unlock(&iort_msi_chip_lock); 5868b4282e6SShameer Kolothum 5878b4282e6SShameer Kolothum return ret; 5888b4282e6SShameer Kolothum } 5898b4282e6SShameer Kolothum 590ae7c1838SHanjun Guo /** 5914bf2efd2STomasz Nowicki * iort_dev_find_its_id() - Find the ITS identifier for a device 5924bf2efd2STomasz Nowicki * @dev: The device. 5936cb6bf56SHanjun Guo * @req_id: Device's requester ID 5944bf2efd2STomasz Nowicki * @idx: Index of the ITS identifier list. 5954bf2efd2STomasz Nowicki * @its_id: ITS identifier. 5964bf2efd2STomasz Nowicki * 5974bf2efd2STomasz Nowicki * Returns: 0 on success, appropriate error value otherwise 5984bf2efd2STomasz Nowicki */ 5994bf2efd2STomasz Nowicki static int iort_dev_find_its_id(struct device *dev, u32 req_id, 6004bf2efd2STomasz Nowicki unsigned int idx, int *its_id) 6014bf2efd2STomasz Nowicki { 6024bf2efd2STomasz Nowicki struct acpi_iort_its_group *its; 6034bf2efd2STomasz Nowicki struct acpi_iort_node *node; 6044bf2efd2STomasz Nowicki 6054bf2efd2STomasz Nowicki node = iort_find_dev_node(dev); 6064bf2efd2STomasz Nowicki if (!node) 6074bf2efd2STomasz Nowicki return -ENXIO; 6084bf2efd2STomasz Nowicki 609697f6093SHanjun Guo node = iort_node_map_id(node, req_id, NULL, IORT_MSI_TYPE); 6104bf2efd2STomasz Nowicki if (!node) 6114bf2efd2STomasz Nowicki return -ENXIO; 6124bf2efd2STomasz Nowicki 6134bf2efd2STomasz Nowicki /* Move to ITS specific data */ 6144bf2efd2STomasz Nowicki its = (struct acpi_iort_its_group *)node->node_data; 6155a46d3f7SLorenzo Pieralisi if (idx >= its->its_count) { 6165a46d3f7SLorenzo Pieralisi dev_err(dev, "requested ITS ID index [%d] overruns ITS entries [%d]\n", 6174bf2efd2STomasz Nowicki idx, its->its_count); 6184bf2efd2STomasz Nowicki return -ENXIO; 6194bf2efd2STomasz Nowicki } 6204bf2efd2STomasz Nowicki 6214bf2efd2STomasz Nowicki *its_id = its->identifiers[idx]; 6224bf2efd2STomasz Nowicki return 0; 6234bf2efd2STomasz Nowicki } 6244bf2efd2STomasz Nowicki 6254bf2efd2STomasz Nowicki /** 6264bf2efd2STomasz Nowicki * iort_get_device_domain() - Find MSI domain related to a device 6274bf2efd2STomasz Nowicki * @dev: The device. 6284bf2efd2STomasz Nowicki * @req_id: Requester ID for the device. 6294bf2efd2STomasz Nowicki * 6304bf2efd2STomasz Nowicki * Returns: the MSI domain for this device, NULL otherwise 6314bf2efd2STomasz Nowicki */ 6324bf2efd2STomasz Nowicki struct irq_domain *iort_get_device_domain(struct device *dev, u32 req_id) 6334bf2efd2STomasz Nowicki { 6344bf2efd2STomasz Nowicki struct fwnode_handle *handle; 6354bf2efd2STomasz Nowicki int its_id; 6364bf2efd2STomasz Nowicki 6374bf2efd2STomasz Nowicki if (iort_dev_find_its_id(dev, req_id, 0, &its_id)) 6384bf2efd2STomasz Nowicki return NULL; 6394bf2efd2STomasz Nowicki 6404bf2efd2STomasz Nowicki handle = iort_find_domain_token(its_id); 6414bf2efd2STomasz Nowicki if (!handle) 6424bf2efd2STomasz Nowicki return NULL; 6434bf2efd2STomasz Nowicki 6444bf2efd2STomasz Nowicki return irq_find_matching_fwnode(handle, DOMAIN_BUS_PCI_MSI); 6454bf2efd2STomasz Nowicki } 6464bf2efd2STomasz Nowicki 64765637901SLorenzo Pieralisi static void iort_set_device_domain(struct device *dev, 64865637901SLorenzo Pieralisi struct acpi_iort_node *node) 64965637901SLorenzo Pieralisi { 65065637901SLorenzo Pieralisi struct acpi_iort_its_group *its; 65165637901SLorenzo Pieralisi struct acpi_iort_node *msi_parent; 65265637901SLorenzo Pieralisi struct acpi_iort_id_mapping *map; 65365637901SLorenzo Pieralisi struct fwnode_handle *iort_fwnode; 65465637901SLorenzo Pieralisi struct irq_domain *domain; 65565637901SLorenzo Pieralisi int index; 65665637901SLorenzo Pieralisi 65765637901SLorenzo Pieralisi index = iort_get_id_mapping_index(node); 65865637901SLorenzo Pieralisi if (index < 0) 65965637901SLorenzo Pieralisi return; 66065637901SLorenzo Pieralisi 66165637901SLorenzo Pieralisi map = ACPI_ADD_PTR(struct acpi_iort_id_mapping, node, 66265637901SLorenzo Pieralisi node->mapping_offset + index * sizeof(*map)); 66365637901SLorenzo Pieralisi 66465637901SLorenzo Pieralisi /* Firmware bug! */ 66565637901SLorenzo Pieralisi if (!map->output_reference || 66665637901SLorenzo Pieralisi !(map->flags & ACPI_IORT_ID_SINGLE_MAPPING)) { 66765637901SLorenzo Pieralisi pr_err(FW_BUG "[node %p type %d] Invalid MSI mapping\n", 66865637901SLorenzo Pieralisi node, node->type); 66965637901SLorenzo Pieralisi return; 67065637901SLorenzo Pieralisi } 67165637901SLorenzo Pieralisi 67265637901SLorenzo Pieralisi msi_parent = ACPI_ADD_PTR(struct acpi_iort_node, iort_table, 67365637901SLorenzo Pieralisi map->output_reference); 67465637901SLorenzo Pieralisi 67565637901SLorenzo Pieralisi if (!msi_parent || msi_parent->type != ACPI_IORT_NODE_ITS_GROUP) 67665637901SLorenzo Pieralisi return; 67765637901SLorenzo Pieralisi 67865637901SLorenzo Pieralisi /* Move to ITS specific data */ 67965637901SLorenzo Pieralisi its = (struct acpi_iort_its_group *)msi_parent->node_data; 68065637901SLorenzo Pieralisi 68165637901SLorenzo Pieralisi iort_fwnode = iort_find_domain_token(its->identifiers[0]); 68265637901SLorenzo Pieralisi if (!iort_fwnode) 68365637901SLorenzo Pieralisi return; 68465637901SLorenzo Pieralisi 68565637901SLorenzo Pieralisi domain = irq_find_matching_fwnode(iort_fwnode, DOMAIN_BUS_PLATFORM_MSI); 68665637901SLorenzo Pieralisi if (domain) 68765637901SLorenzo Pieralisi dev_set_msi_domain(dev, domain); 68865637901SLorenzo Pieralisi } 68965637901SLorenzo Pieralisi 690d4f54a18SHanjun Guo /** 691d4f54a18SHanjun Guo * iort_get_platform_device_domain() - Find MSI domain related to a 692d4f54a18SHanjun Guo * platform device 693d4f54a18SHanjun Guo * @dev: the dev pointer associated with the platform device 694d4f54a18SHanjun Guo * 695d4f54a18SHanjun Guo * Returns: the MSI domain for this device, NULL otherwise 696d4f54a18SHanjun Guo */ 697d4f54a18SHanjun Guo static struct irq_domain *iort_get_platform_device_domain(struct device *dev) 698d4f54a18SHanjun Guo { 699ea2412dcSLorenzo Pieralisi struct acpi_iort_node *node, *msi_parent = NULL; 700d4f54a18SHanjun Guo struct fwnode_handle *iort_fwnode; 701d4f54a18SHanjun Guo struct acpi_iort_its_group *its; 702d4f54a18SHanjun Guo int i; 703d4f54a18SHanjun Guo 704d4f54a18SHanjun Guo /* find its associated iort node */ 705d4f54a18SHanjun Guo node = iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT, 706d4f54a18SHanjun Guo iort_match_node_callback, dev); 707d4f54a18SHanjun Guo if (!node) 708d4f54a18SHanjun Guo return NULL; 709d4f54a18SHanjun Guo 710d4f54a18SHanjun Guo /* then find its msi parent node */ 711d4f54a18SHanjun Guo for (i = 0; i < node->mapping_count; i++) { 712d4f54a18SHanjun Guo msi_parent = iort_node_map_platform_id(node, NULL, 713d4f54a18SHanjun Guo IORT_MSI_TYPE, i); 714d4f54a18SHanjun Guo if (msi_parent) 715d4f54a18SHanjun Guo break; 716d4f54a18SHanjun Guo } 717d4f54a18SHanjun Guo 718d4f54a18SHanjun Guo if (!msi_parent) 719d4f54a18SHanjun Guo return NULL; 720d4f54a18SHanjun Guo 721d4f54a18SHanjun Guo /* Move to ITS specific data */ 722d4f54a18SHanjun Guo its = (struct acpi_iort_its_group *)msi_parent->node_data; 723d4f54a18SHanjun Guo 724d4f54a18SHanjun Guo iort_fwnode = iort_find_domain_token(its->identifiers[0]); 725d4f54a18SHanjun Guo if (!iort_fwnode) 726d4f54a18SHanjun Guo return NULL; 727d4f54a18SHanjun Guo 728d4f54a18SHanjun Guo return irq_find_matching_fwnode(iort_fwnode, DOMAIN_BUS_PLATFORM_MSI); 729d4f54a18SHanjun Guo } 730d4f54a18SHanjun Guo 731d4f54a18SHanjun Guo void acpi_configure_pmsi_domain(struct device *dev) 732d4f54a18SHanjun Guo { 733d4f54a18SHanjun Guo struct irq_domain *msi_domain; 734d4f54a18SHanjun Guo 735d4f54a18SHanjun Guo msi_domain = iort_get_platform_device_domain(dev); 736d4f54a18SHanjun Guo if (msi_domain) 737d4f54a18SHanjun Guo dev_set_msi_domain(dev, msi_domain); 738d4f54a18SHanjun Guo } 739d4f54a18SHanjun Guo 740bc8648d4SRobin Murphy static int __maybe_unused __get_pci_rid(struct pci_dev *pdev, u16 alias, 741bc8648d4SRobin Murphy void *data) 742643b8e4dSLorenzo Pieralisi { 743643b8e4dSLorenzo Pieralisi u32 *rid = data; 744643b8e4dSLorenzo Pieralisi 745643b8e4dSLorenzo Pieralisi *rid = alias; 746643b8e4dSLorenzo Pieralisi return 0; 747643b8e4dSLorenzo Pieralisi } 748643b8e4dSLorenzo Pieralisi 749d49f2dedSLorenzo Pieralisi #ifdef CONFIG_IOMMU_API 7508b4282e6SShameer Kolothum static struct acpi_iort_node *iort_get_msi_resv_iommu(struct device *dev) 7518b4282e6SShameer Kolothum { 7528b4282e6SShameer Kolothum struct acpi_iort_node *iommu; 7538097e53eSJoerg Roedel struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); 7548b4282e6SShameer Kolothum 7558b4282e6SShameer Kolothum iommu = iort_get_iort_node(fwspec->iommu_fwnode); 7568b4282e6SShameer Kolothum 7578b4282e6SShameer Kolothum if (iommu && (iommu->type == ACPI_IORT_NODE_SMMU_V3)) { 7588b4282e6SShameer Kolothum struct acpi_iort_smmu_v3 *smmu; 7598b4282e6SShameer Kolothum 7608b4282e6SShameer Kolothum smmu = (struct acpi_iort_smmu_v3 *)iommu->node_data; 7618b4282e6SShameer Kolothum if (smmu->model == ACPI_IORT_SMMU_V3_HISILICON_HI161X) 7628b4282e6SShameer Kolothum return iommu; 7638b4282e6SShameer Kolothum } 7648b4282e6SShameer Kolothum 7658b4282e6SShameer Kolothum return NULL; 7668b4282e6SShameer Kolothum } 7678b4282e6SShameer Kolothum 7688097e53eSJoerg Roedel static inline const struct iommu_ops *iort_fwspec_iommu_ops(struct device *dev) 769d49f2dedSLorenzo Pieralisi { 7708097e53eSJoerg Roedel struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); 7718097e53eSJoerg Roedel 772d49f2dedSLorenzo Pieralisi return (fwspec && fwspec->ops) ? fwspec->ops : NULL; 773d49f2dedSLorenzo Pieralisi } 774d49f2dedSLorenzo Pieralisi 775e3d49392SLorenzo Pieralisi static inline int iort_add_device_replay(const struct iommu_ops *ops, 776e3d49392SLorenzo Pieralisi struct device *dev) 777d49f2dedSLorenzo Pieralisi { 778d49f2dedSLorenzo Pieralisi int err = 0; 779d49f2dedSLorenzo Pieralisi 780d2e1a003SJoerg Roedel if (dev->bus && !device_iommu_mapped(dev)) 781d2e1a003SJoerg Roedel err = iommu_probe_device(dev); 782d49f2dedSLorenzo Pieralisi 783d49f2dedSLorenzo Pieralisi return err; 784d49f2dedSLorenzo Pieralisi } 7858b4282e6SShameer Kolothum 7868b4282e6SShameer Kolothum /** 7878b4282e6SShameer Kolothum * iort_iommu_msi_get_resv_regions - Reserved region driver helper 7888b4282e6SShameer Kolothum * @dev: Device from iommu_get_resv_regions() 7898b4282e6SShameer Kolothum * @head: Reserved region list from iommu_get_resv_regions() 7908b4282e6SShameer Kolothum * 7918b4282e6SShameer Kolothum * Returns: Number of msi reserved regions on success (0 if platform 7928b4282e6SShameer Kolothum * doesn't require the reservation or no associated msi regions), 7938b4282e6SShameer Kolothum * appropriate error value otherwise. The ITS interrupt translation 7948b4282e6SShameer Kolothum * spaces (ITS_base + SZ_64K, SZ_64K) associated with the device 7958b4282e6SShameer Kolothum * are the msi reserved regions. 7968b4282e6SShameer Kolothum */ 7978b4282e6SShameer Kolothum int iort_iommu_msi_get_resv_regions(struct device *dev, struct list_head *head) 7988b4282e6SShameer Kolothum { 7998097e53eSJoerg Roedel struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); 8008b4282e6SShameer Kolothum struct acpi_iort_its_group *its; 8018b4282e6SShameer Kolothum struct acpi_iort_node *iommu_node, *its_node = NULL; 8028b4282e6SShameer Kolothum int i, resv = 0; 8038b4282e6SShameer Kolothum 8048b4282e6SShameer Kolothum iommu_node = iort_get_msi_resv_iommu(dev); 8058b4282e6SShameer Kolothum if (!iommu_node) 8068b4282e6SShameer Kolothum return 0; 8078b4282e6SShameer Kolothum 8088b4282e6SShameer Kolothum /* 8098b4282e6SShameer Kolothum * Current logic to reserve ITS regions relies on HW topologies 8108b4282e6SShameer Kolothum * where a given PCI or named component maps its IDs to only one 8118b4282e6SShameer Kolothum * ITS group; if a PCI or named component can map its IDs to 8128b4282e6SShameer Kolothum * different ITS groups through IORT mappings this function has 8138b4282e6SShameer Kolothum * to be reworked to ensure we reserve regions for all ITS groups 8148b4282e6SShameer Kolothum * a given PCI or named component may map IDs to. 8158b4282e6SShameer Kolothum */ 8168b4282e6SShameer Kolothum 8178097e53eSJoerg Roedel for (i = 0; i < fwspec->num_ids; i++) { 8188b4282e6SShameer Kolothum its_node = iort_node_map_id(iommu_node, 8198097e53eSJoerg Roedel fwspec->ids[i], 8208b4282e6SShameer Kolothum NULL, IORT_MSI_TYPE); 8218b4282e6SShameer Kolothum if (its_node) 8228b4282e6SShameer Kolothum break; 8238b4282e6SShameer Kolothum } 8248b4282e6SShameer Kolothum 8258b4282e6SShameer Kolothum if (!its_node) 8268b4282e6SShameer Kolothum return 0; 8278b4282e6SShameer Kolothum 8288b4282e6SShameer Kolothum /* Move to ITS specific data */ 8298b4282e6SShameer Kolothum its = (struct acpi_iort_its_group *)its_node->node_data; 8308b4282e6SShameer Kolothum 8318b4282e6SShameer Kolothum for (i = 0; i < its->its_count; i++) { 8328b4282e6SShameer Kolothum phys_addr_t base; 8338b4282e6SShameer Kolothum 8348b4282e6SShameer Kolothum if (!iort_find_its_base(its->identifiers[i], &base)) { 8358b4282e6SShameer Kolothum int prot = IOMMU_WRITE | IOMMU_NOEXEC | IOMMU_MMIO; 8368b4282e6SShameer Kolothum struct iommu_resv_region *region; 8378b4282e6SShameer Kolothum 8388b4282e6SShameer Kolothum region = iommu_alloc_resv_region(base + SZ_64K, SZ_64K, 8398b4282e6SShameer Kolothum prot, IOMMU_RESV_MSI); 8408b4282e6SShameer Kolothum if (region) { 8418b4282e6SShameer Kolothum list_add_tail(®ion->list, head); 8428b4282e6SShameer Kolothum resv++; 8438b4282e6SShameer Kolothum } 8448b4282e6SShameer Kolothum } 8458b4282e6SShameer Kolothum } 8468b4282e6SShameer Kolothum 8478b4282e6SShameer Kolothum return (resv == its->its_count) ? resv : -ENODEV; 8488b4282e6SShameer Kolothum } 84982126886SLorenzo Pieralisi 85082126886SLorenzo Pieralisi static inline bool iort_iommu_driver_enabled(u8 type) 85182126886SLorenzo Pieralisi { 85282126886SLorenzo Pieralisi switch (type) { 85382126886SLorenzo Pieralisi case ACPI_IORT_NODE_SMMU_V3: 854d3daf666SArd Biesheuvel return IS_ENABLED(CONFIG_ARM_SMMU_V3); 85582126886SLorenzo Pieralisi case ACPI_IORT_NODE_SMMU: 856d3daf666SArd Biesheuvel return IS_ENABLED(CONFIG_ARM_SMMU); 85782126886SLorenzo Pieralisi default: 85882126886SLorenzo Pieralisi pr_warn("IORT node type %u does not describe an SMMU\n", type); 85982126886SLorenzo Pieralisi return false; 86082126886SLorenzo Pieralisi } 86182126886SLorenzo Pieralisi } 86282126886SLorenzo Pieralisi 86382126886SLorenzo Pieralisi static int arm_smmu_iort_xlate(struct device *dev, u32 streamid, 86482126886SLorenzo Pieralisi struct fwnode_handle *fwnode, 86582126886SLorenzo Pieralisi const struct iommu_ops *ops) 86682126886SLorenzo Pieralisi { 86782126886SLorenzo Pieralisi int ret = iommu_fwspec_init(dev, fwnode, ops); 86882126886SLorenzo Pieralisi 86982126886SLorenzo Pieralisi if (!ret) 87082126886SLorenzo Pieralisi ret = iommu_fwspec_add_ids(dev, &streamid, 1); 87182126886SLorenzo Pieralisi 87282126886SLorenzo Pieralisi return ret; 87382126886SLorenzo Pieralisi } 87482126886SLorenzo Pieralisi 87582126886SLorenzo Pieralisi static bool iort_pci_rc_supports_ats(struct acpi_iort_node *node) 87682126886SLorenzo Pieralisi { 87782126886SLorenzo Pieralisi struct acpi_iort_root_complex *pci_rc; 87882126886SLorenzo Pieralisi 87982126886SLorenzo Pieralisi pci_rc = (struct acpi_iort_root_complex *)node->node_data; 88082126886SLorenzo Pieralisi return pci_rc->ats_attribute & ACPI_IORT_ATS_SUPPORTED; 88182126886SLorenzo Pieralisi } 882d49f2dedSLorenzo Pieralisi 883bc8648d4SRobin Murphy static int iort_iommu_xlate(struct device *dev, struct acpi_iort_node *node, 884643b8e4dSLorenzo Pieralisi u32 streamid) 885643b8e4dSLorenzo Pieralisi { 886bc8648d4SRobin Murphy const struct iommu_ops *ops; 887643b8e4dSLorenzo Pieralisi struct fwnode_handle *iort_fwnode; 888643b8e4dSLorenzo Pieralisi 889bc8648d4SRobin Murphy if (!node) 890bc8648d4SRobin Murphy return -ENODEV; 891bc8648d4SRobin Murphy 892643b8e4dSLorenzo Pieralisi iort_fwnode = iort_get_fwnode(node); 893643b8e4dSLorenzo Pieralisi if (!iort_fwnode) 894bc8648d4SRobin Murphy return -ENODEV; 895643b8e4dSLorenzo Pieralisi 8965a1bb638SSricharan R /* 8975a1bb638SSricharan R * If the ops look-up fails, this means that either 8985a1bb638SSricharan R * the SMMU drivers have not been probed yet or that 8995a1bb638SSricharan R * the SMMU drivers are not built in the kernel; 9005a1bb638SSricharan R * Depending on whether the SMMU drivers are built-in 9015a1bb638SSricharan R * in the kernel or not, defer the IOMMU configuration 9025a1bb638SSricharan R * or just abort it. 9035a1bb638SSricharan R */ 904bc8648d4SRobin Murphy ops = iommu_ops_from_fwnode(iort_fwnode); 905643b8e4dSLorenzo Pieralisi if (!ops) 9065a1bb638SSricharan R return iort_iommu_driver_enabled(node->type) ? 907bc8648d4SRobin Murphy -EPROBE_DEFER : -ENODEV; 908643b8e4dSLorenzo Pieralisi 909bc8648d4SRobin Murphy return arm_smmu_iort_xlate(dev, streamid, iort_fwnode, ops); 910643b8e4dSLorenzo Pieralisi } 911643b8e4dSLorenzo Pieralisi 912bc8648d4SRobin Murphy struct iort_pci_alias_info { 913bc8648d4SRobin Murphy struct device *dev; 914bc8648d4SRobin Murphy struct acpi_iort_node *node; 915bc8648d4SRobin Murphy }; 916bc8648d4SRobin Murphy 917bc8648d4SRobin Murphy static int iort_pci_iommu_init(struct pci_dev *pdev, u16 alias, void *data) 918bc8648d4SRobin Murphy { 919bc8648d4SRobin Murphy struct iort_pci_alias_info *info = data; 920bc8648d4SRobin Murphy struct acpi_iort_node *parent; 921bc8648d4SRobin Murphy u32 streamid; 922bc8648d4SRobin Murphy 923bc8648d4SRobin Murphy parent = iort_node_map_id(info->node, alias, &streamid, 924bc8648d4SRobin Murphy IORT_IOMMU_TYPE); 925bc8648d4SRobin Murphy return iort_iommu_xlate(info->dev, parent, streamid); 926643b8e4dSLorenzo Pieralisi } 927643b8e4dSLorenzo Pieralisi 928da22565dSJean-Philippe Brucker static void iort_named_component_init(struct device *dev, 929da22565dSJean-Philippe Brucker struct acpi_iort_node *node) 930da22565dSJean-Philippe Brucker { 931da22565dSJean-Philippe Brucker struct acpi_iort_named_component *nc; 932da22565dSJean-Philippe Brucker struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); 933da22565dSJean-Philippe Brucker 934da22565dSJean-Philippe Brucker if (!fwspec) 935da22565dSJean-Philippe Brucker return; 936da22565dSJean-Philippe Brucker 937da22565dSJean-Philippe Brucker nc = (struct acpi_iort_named_component *)node->node_data; 938da22565dSJean-Philippe Brucker fwspec->num_pasid_bits = FIELD_GET(ACPI_IORT_NC_PASID_BITS, 939da22565dSJean-Philippe Brucker nc->node_flags); 940da22565dSJean-Philippe Brucker } 941da22565dSJean-Philippe Brucker 94282126886SLorenzo Pieralisi /** 94382126886SLorenzo Pieralisi * iort_iommu_configure - Set-up IOMMU configuration for a device. 94482126886SLorenzo Pieralisi * 94582126886SLorenzo Pieralisi * @dev: device to configure 94682126886SLorenzo Pieralisi * 94782126886SLorenzo Pieralisi * Returns: iommu_ops pointer on configuration success 94882126886SLorenzo Pieralisi * NULL on configuration failure 94982126886SLorenzo Pieralisi */ 95082126886SLorenzo Pieralisi const struct iommu_ops *iort_iommu_configure(struct device *dev) 95182126886SLorenzo Pieralisi { 95282126886SLorenzo Pieralisi struct acpi_iort_node *node, *parent; 95382126886SLorenzo Pieralisi const struct iommu_ops *ops; 95482126886SLorenzo Pieralisi u32 streamid = 0; 95582126886SLorenzo Pieralisi int err = -ENODEV; 95682126886SLorenzo Pieralisi 95782126886SLorenzo Pieralisi /* 95882126886SLorenzo Pieralisi * If we already translated the fwspec there 95982126886SLorenzo Pieralisi * is nothing left to do, return the iommu_ops. 96082126886SLorenzo Pieralisi */ 96182126886SLorenzo Pieralisi ops = iort_fwspec_iommu_ops(dev); 96282126886SLorenzo Pieralisi if (ops) 96382126886SLorenzo Pieralisi return ops; 96482126886SLorenzo Pieralisi 96582126886SLorenzo Pieralisi if (dev_is_pci(dev)) { 9666990ec79SJoerg Roedel struct iommu_fwspec *fwspec; 96782126886SLorenzo Pieralisi struct pci_bus *bus = to_pci_dev(dev)->bus; 96882126886SLorenzo Pieralisi struct iort_pci_alias_info info = { .dev = dev }; 96982126886SLorenzo Pieralisi 97082126886SLorenzo Pieralisi node = iort_scan_node(ACPI_IORT_NODE_PCI_ROOT_COMPLEX, 97182126886SLorenzo Pieralisi iort_match_node_callback, &bus->dev); 97282126886SLorenzo Pieralisi if (!node) 97382126886SLorenzo Pieralisi return NULL; 97482126886SLorenzo Pieralisi 97582126886SLorenzo Pieralisi info.node = node; 97682126886SLorenzo Pieralisi err = pci_for_each_dma_alias(to_pci_dev(dev), 97782126886SLorenzo Pieralisi iort_pci_iommu_init, &info); 97882126886SLorenzo Pieralisi 9796990ec79SJoerg Roedel fwspec = dev_iommu_fwspec_get(dev); 9806990ec79SJoerg Roedel if (fwspec && iort_pci_rc_supports_ats(node)) 9816990ec79SJoerg Roedel fwspec->flags |= IOMMU_FWSPEC_PCI_RC_ATS; 98282126886SLorenzo Pieralisi } else { 98382126886SLorenzo Pieralisi int i = 0; 98482126886SLorenzo Pieralisi 98582126886SLorenzo Pieralisi node = iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT, 98682126886SLorenzo Pieralisi iort_match_node_callback, dev); 98782126886SLorenzo Pieralisi if (!node) 98882126886SLorenzo Pieralisi return NULL; 98982126886SLorenzo Pieralisi 99082126886SLorenzo Pieralisi do { 99182126886SLorenzo Pieralisi parent = iort_node_map_platform_id(node, &streamid, 99282126886SLorenzo Pieralisi IORT_IOMMU_TYPE, 99382126886SLorenzo Pieralisi i++); 99482126886SLorenzo Pieralisi 99582126886SLorenzo Pieralisi if (parent) 99682126886SLorenzo Pieralisi err = iort_iommu_xlate(dev, parent, streamid); 99782126886SLorenzo Pieralisi } while (parent && !err); 998da22565dSJean-Philippe Brucker 999da22565dSJean-Philippe Brucker if (!err) 1000da22565dSJean-Philippe Brucker iort_named_component_init(dev, node); 100182126886SLorenzo Pieralisi } 100282126886SLorenzo Pieralisi 100382126886SLorenzo Pieralisi /* 100482126886SLorenzo Pieralisi * If we have reason to believe the IOMMU driver missed the initial 100582126886SLorenzo Pieralisi * add_device callback for dev, replay it to get things in order. 100682126886SLorenzo Pieralisi */ 100782126886SLorenzo Pieralisi if (!err) { 100882126886SLorenzo Pieralisi ops = iort_fwspec_iommu_ops(dev); 100982126886SLorenzo Pieralisi err = iort_add_device_replay(ops, dev); 101082126886SLorenzo Pieralisi } 101182126886SLorenzo Pieralisi 101282126886SLorenzo Pieralisi /* Ignore all other errors apart from EPROBE_DEFER */ 101382126886SLorenzo Pieralisi if (err == -EPROBE_DEFER) { 101482126886SLorenzo Pieralisi ops = ERR_PTR(err); 101582126886SLorenzo Pieralisi } else if (err) { 101682126886SLorenzo Pieralisi dev_dbg(dev, "Adding to IOMMU failed: %d\n", err); 101782126886SLorenzo Pieralisi ops = NULL; 101882126886SLorenzo Pieralisi } 101982126886SLorenzo Pieralisi 102082126886SLorenzo Pieralisi return ops; 102182126886SLorenzo Pieralisi } 102282126886SLorenzo Pieralisi #else 102382126886SLorenzo Pieralisi static inline const struct iommu_ops *iort_fwspec_iommu_ops(struct device *dev) 102482126886SLorenzo Pieralisi { return NULL; } 102582126886SLorenzo Pieralisi static inline int iort_add_device_replay(const struct iommu_ops *ops, 102682126886SLorenzo Pieralisi struct device *dev) 102782126886SLorenzo Pieralisi { return 0; } 102882126886SLorenzo Pieralisi int iort_iommu_msi_get_resv_regions(struct device *dev, struct list_head *head) 102982126886SLorenzo Pieralisi { return 0; } 103082126886SLorenzo Pieralisi const struct iommu_ops *iort_iommu_configure(struct device *dev) 103182126886SLorenzo Pieralisi { return NULL; } 103282126886SLorenzo Pieralisi #endif 103382126886SLorenzo Pieralisi 103410d8ab2cSLorenzo Pieralisi static int nc_dma_get_range(struct device *dev, u64 *size) 103510d8ab2cSLorenzo Pieralisi { 103610d8ab2cSLorenzo Pieralisi struct acpi_iort_node *node; 103710d8ab2cSLorenzo Pieralisi struct acpi_iort_named_component *ncomp; 103810d8ab2cSLorenzo Pieralisi 103910d8ab2cSLorenzo Pieralisi node = iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT, 104010d8ab2cSLorenzo Pieralisi iort_match_node_callback, dev); 104110d8ab2cSLorenzo Pieralisi if (!node) 104210d8ab2cSLorenzo Pieralisi return -ENODEV; 104310d8ab2cSLorenzo Pieralisi 104410d8ab2cSLorenzo Pieralisi ncomp = (struct acpi_iort_named_component *)node->node_data; 104510d8ab2cSLorenzo Pieralisi 104610d8ab2cSLorenzo Pieralisi *size = ncomp->memory_address_limit >= 64 ? U64_MAX : 104710d8ab2cSLorenzo Pieralisi 1ULL<<ncomp->memory_address_limit; 104810d8ab2cSLorenzo Pieralisi 104910d8ab2cSLorenzo Pieralisi return 0; 105010d8ab2cSLorenzo Pieralisi } 105110d8ab2cSLorenzo Pieralisi 10525ac65e8cSRobin Murphy static int rc_dma_get_range(struct device *dev, u64 *size) 10535ac65e8cSRobin Murphy { 10545ac65e8cSRobin Murphy struct acpi_iort_node *node; 10555ac65e8cSRobin Murphy struct acpi_iort_root_complex *rc; 1056c7777236SJean-Philippe Brucker struct pci_bus *pbus = to_pci_dev(dev)->bus; 10575ac65e8cSRobin Murphy 10585ac65e8cSRobin Murphy node = iort_scan_node(ACPI_IORT_NODE_PCI_ROOT_COMPLEX, 1059c7777236SJean-Philippe Brucker iort_match_node_callback, &pbus->dev); 10605ac65e8cSRobin Murphy if (!node || node->revision < 1) 10615ac65e8cSRobin Murphy return -ENODEV; 10625ac65e8cSRobin Murphy 10635ac65e8cSRobin Murphy rc = (struct acpi_iort_root_complex *)node->node_data; 10645ac65e8cSRobin Murphy 10655ac65e8cSRobin Murphy *size = rc->memory_address_limit >= 64 ? U64_MAX : 10665ac65e8cSRobin Murphy 1ULL<<rc->memory_address_limit; 10675ac65e8cSRobin Murphy 10685ac65e8cSRobin Murphy return 0; 10695ac65e8cSRobin Murphy } 10705ac65e8cSRobin Murphy 1071643b8e4dSLorenzo Pieralisi /** 10727ad42639SLorenzo Pieralisi * iort_dma_setup() - Set-up device DMA parameters. 107318b709beSLorenzo Pieralisi * 107418b709beSLorenzo Pieralisi * @dev: device to configure 10757ad42639SLorenzo Pieralisi * @dma_addr: device DMA address result pointer 10767ad42639SLorenzo Pieralisi * @size: DMA range size result pointer 107718b709beSLorenzo Pieralisi */ 10787ad42639SLorenzo Pieralisi void iort_dma_setup(struct device *dev, u64 *dma_addr, u64 *dma_size) 107918b709beSLorenzo Pieralisi { 1080a7ba70f1SNicolas Saenz Julienne u64 end, mask, dmaaddr = 0, size = 0, offset = 0; 1081a7ba70f1SNicolas Saenz Julienne int ret; 10827ad42639SLorenzo Pieralisi 108318b709beSLorenzo Pieralisi /* 10846757cdaeSRobin Murphy * If @dev is expected to be DMA-capable then the bus code that created 10856757cdaeSRobin Murphy * it should have initialised its dma_mask pointer by this point. For 10866757cdaeSRobin Murphy * now, we'll continue the legacy behaviour of coercing it to the 10876757cdaeSRobin Murphy * coherent mask if not, but we'll no longer do so quietly. 108818b709beSLorenzo Pieralisi */ 10896757cdaeSRobin Murphy if (!dev->dma_mask) { 10906757cdaeSRobin Murphy dev_warn(dev, "DMA mask not set\n"); 109118b709beSLorenzo Pieralisi dev->dma_mask = &dev->coherent_dma_mask; 10926757cdaeSRobin Murphy } 10937ad42639SLorenzo Pieralisi 10946757cdaeSRobin Murphy if (dev->coherent_dma_mask) 10957ad42639SLorenzo Pieralisi size = max(dev->coherent_dma_mask, dev->coherent_dma_mask + 1); 10966757cdaeSRobin Murphy else 10976757cdaeSRobin Murphy size = 1ULL << 32; 10987ad42639SLorenzo Pieralisi 10997ad42639SLorenzo Pieralisi ret = acpi_dma_get_range(dev, &dmaaddr, &offset, &size); 11005ac65e8cSRobin Murphy if (ret == -ENODEV) 11017fb89e1dSArd Biesheuvel ret = dev_is_pci(dev) ? rc_dma_get_range(dev, &size) 11027fb89e1dSArd Biesheuvel : nc_dma_get_range(dev, &size); 110310d8ab2cSLorenzo Pieralisi 11047ad42639SLorenzo Pieralisi if (!ret) { 11057ad42639SLorenzo Pieralisi /* 1106a7ba70f1SNicolas Saenz Julienne * Limit coherent and dma mask based on size retrieved from 1107a7ba70f1SNicolas Saenz Julienne * firmware. 11087ad42639SLorenzo Pieralisi */ 1109a7ba70f1SNicolas Saenz Julienne end = dmaaddr + size - 1; 1110a7ba70f1SNicolas Saenz Julienne mask = DMA_BIT_MASK(ilog2(end) + 1); 1111a7ba70f1SNicolas Saenz Julienne dev->bus_dma_limit = end; 11127ad42639SLorenzo Pieralisi dev->coherent_dma_mask = mask; 11137ad42639SLorenzo Pieralisi *dev->dma_mask = mask; 11147ad42639SLorenzo Pieralisi } 11157ad42639SLorenzo Pieralisi 11167ad42639SLorenzo Pieralisi *dma_addr = dmaaddr; 11177ad42639SLorenzo Pieralisi *dma_size = size; 11187ad42639SLorenzo Pieralisi 11197ad42639SLorenzo Pieralisi dev->dma_pfn_offset = PFN_DOWN(offset); 11207ad42639SLorenzo Pieralisi dev_dbg(dev, "dma_pfn_offset(%#08llx)\n", offset); 112118b709beSLorenzo Pieralisi } 112218b709beSLorenzo Pieralisi 1123e4dadfa8SLorenzo Pieralisi static void __init acpi_iort_register_irq(int hwirq, const char *name, 1124e4dadfa8SLorenzo Pieralisi int trigger, 1125e4dadfa8SLorenzo Pieralisi struct resource *res) 1126e4dadfa8SLorenzo Pieralisi { 1127e4dadfa8SLorenzo Pieralisi int irq = acpi_register_gsi(NULL, hwirq, trigger, 1128e4dadfa8SLorenzo Pieralisi ACPI_ACTIVE_HIGH); 1129e4dadfa8SLorenzo Pieralisi 1130e4dadfa8SLorenzo Pieralisi if (irq <= 0) { 1131e4dadfa8SLorenzo Pieralisi pr_err("could not register gsi hwirq %d name [%s]\n", hwirq, 1132e4dadfa8SLorenzo Pieralisi name); 1133e4dadfa8SLorenzo Pieralisi return; 1134e4dadfa8SLorenzo Pieralisi } 1135e4dadfa8SLorenzo Pieralisi 1136e4dadfa8SLorenzo Pieralisi res->start = irq; 1137e4dadfa8SLorenzo Pieralisi res->end = irq; 1138e4dadfa8SLorenzo Pieralisi res->flags = IORESOURCE_IRQ; 1139e4dadfa8SLorenzo Pieralisi res->name = name; 1140e4dadfa8SLorenzo Pieralisi } 1141e4dadfa8SLorenzo Pieralisi 1142e4dadfa8SLorenzo Pieralisi static int __init arm_smmu_v3_count_resources(struct acpi_iort_node *node) 1143e4dadfa8SLorenzo Pieralisi { 1144e4dadfa8SLorenzo Pieralisi struct acpi_iort_smmu_v3 *smmu; 1145e4dadfa8SLorenzo Pieralisi /* Always present mem resource */ 1146e4dadfa8SLorenzo Pieralisi int num_res = 1; 1147e4dadfa8SLorenzo Pieralisi 1148e4dadfa8SLorenzo Pieralisi /* Retrieve SMMUv3 specific data */ 1149e4dadfa8SLorenzo Pieralisi smmu = (struct acpi_iort_smmu_v3 *)node->node_data; 1150e4dadfa8SLorenzo Pieralisi 1151e4dadfa8SLorenzo Pieralisi if (smmu->event_gsiv) 1152e4dadfa8SLorenzo Pieralisi num_res++; 1153e4dadfa8SLorenzo Pieralisi 1154e4dadfa8SLorenzo Pieralisi if (smmu->pri_gsiv) 1155e4dadfa8SLorenzo Pieralisi num_res++; 1156e4dadfa8SLorenzo Pieralisi 1157e4dadfa8SLorenzo Pieralisi if (smmu->gerr_gsiv) 1158e4dadfa8SLorenzo Pieralisi num_res++; 1159e4dadfa8SLorenzo Pieralisi 1160e4dadfa8SLorenzo Pieralisi if (smmu->sync_gsiv) 1161e4dadfa8SLorenzo Pieralisi num_res++; 1162e4dadfa8SLorenzo Pieralisi 1163e4dadfa8SLorenzo Pieralisi return num_res; 1164e4dadfa8SLorenzo Pieralisi } 1165e4dadfa8SLorenzo Pieralisi 1166f935448aSGeetha Sowjanya static bool arm_smmu_v3_is_combined_irq(struct acpi_iort_smmu_v3 *smmu) 1167f935448aSGeetha Sowjanya { 1168f935448aSGeetha Sowjanya /* 1169f935448aSGeetha Sowjanya * Cavium ThunderX2 implementation doesn't not support unique 1170f935448aSGeetha Sowjanya * irq line. Use single irq line for all the SMMUv3 interrupts. 1171f935448aSGeetha Sowjanya */ 1172f935448aSGeetha Sowjanya if (smmu->model != ACPI_IORT_SMMU_V3_CAVIUM_CN99XX) 1173f935448aSGeetha Sowjanya return false; 1174f935448aSGeetha Sowjanya 1175f935448aSGeetha Sowjanya /* 1176f935448aSGeetha Sowjanya * ThunderX2 doesn't support MSIs from the SMMU, so we're checking 1177f935448aSGeetha Sowjanya * SPI numbers here. 1178f935448aSGeetha Sowjanya */ 1179f935448aSGeetha Sowjanya return smmu->event_gsiv == smmu->pri_gsiv && 1180f935448aSGeetha Sowjanya smmu->event_gsiv == smmu->gerr_gsiv && 1181f935448aSGeetha Sowjanya smmu->event_gsiv == smmu->sync_gsiv; 1182f935448aSGeetha Sowjanya } 1183f935448aSGeetha Sowjanya 1184403e8c7cSLinu Cherian static unsigned long arm_smmu_v3_resource_size(struct acpi_iort_smmu_v3 *smmu) 1185403e8c7cSLinu Cherian { 1186403e8c7cSLinu Cherian /* 1187403e8c7cSLinu Cherian * Override the size, for Cavium ThunderX2 implementation 1188403e8c7cSLinu Cherian * which doesn't support the page 1 SMMU register space. 1189403e8c7cSLinu Cherian */ 1190403e8c7cSLinu Cherian if (smmu->model == ACPI_IORT_SMMU_V3_CAVIUM_CN99XX) 1191403e8c7cSLinu Cherian return SZ_64K; 1192403e8c7cSLinu Cherian 1193403e8c7cSLinu Cherian return SZ_128K; 1194403e8c7cSLinu Cherian } 1195403e8c7cSLinu Cherian 1196e4dadfa8SLorenzo Pieralisi static void __init arm_smmu_v3_init_resources(struct resource *res, 1197e4dadfa8SLorenzo Pieralisi struct acpi_iort_node *node) 1198e4dadfa8SLorenzo Pieralisi { 1199e4dadfa8SLorenzo Pieralisi struct acpi_iort_smmu_v3 *smmu; 1200e4dadfa8SLorenzo Pieralisi int num_res = 0; 1201e4dadfa8SLorenzo Pieralisi 1202e4dadfa8SLorenzo Pieralisi /* Retrieve SMMUv3 specific data */ 1203e4dadfa8SLorenzo Pieralisi smmu = (struct acpi_iort_smmu_v3 *)node->node_data; 1204e4dadfa8SLorenzo Pieralisi 1205e4dadfa8SLorenzo Pieralisi res[num_res].start = smmu->base_address; 1206403e8c7cSLinu Cherian res[num_res].end = smmu->base_address + 1207403e8c7cSLinu Cherian arm_smmu_v3_resource_size(smmu) - 1; 1208e4dadfa8SLorenzo Pieralisi res[num_res].flags = IORESOURCE_MEM; 1209e4dadfa8SLorenzo Pieralisi 1210e4dadfa8SLorenzo Pieralisi num_res++; 1211f935448aSGeetha Sowjanya if (arm_smmu_v3_is_combined_irq(smmu)) { 1212f935448aSGeetha Sowjanya if (smmu->event_gsiv) 1213f935448aSGeetha Sowjanya acpi_iort_register_irq(smmu->event_gsiv, "combined", 1214f935448aSGeetha Sowjanya ACPI_EDGE_SENSITIVE, 1215f935448aSGeetha Sowjanya &res[num_res++]); 1216f935448aSGeetha Sowjanya } else { 1217e4dadfa8SLorenzo Pieralisi 1218e4dadfa8SLorenzo Pieralisi if (smmu->event_gsiv) 1219e4dadfa8SLorenzo Pieralisi acpi_iort_register_irq(smmu->event_gsiv, "eventq", 1220e4dadfa8SLorenzo Pieralisi ACPI_EDGE_SENSITIVE, 1221e4dadfa8SLorenzo Pieralisi &res[num_res++]); 1222e4dadfa8SLorenzo Pieralisi 1223e4dadfa8SLorenzo Pieralisi if (smmu->pri_gsiv) 1224e4dadfa8SLorenzo Pieralisi acpi_iort_register_irq(smmu->pri_gsiv, "priq", 1225e4dadfa8SLorenzo Pieralisi ACPI_EDGE_SENSITIVE, 1226e4dadfa8SLorenzo Pieralisi &res[num_res++]); 1227e4dadfa8SLorenzo Pieralisi 1228e4dadfa8SLorenzo Pieralisi if (smmu->gerr_gsiv) 1229e4dadfa8SLorenzo Pieralisi acpi_iort_register_irq(smmu->gerr_gsiv, "gerror", 1230e4dadfa8SLorenzo Pieralisi ACPI_EDGE_SENSITIVE, 1231e4dadfa8SLorenzo Pieralisi &res[num_res++]); 1232e4dadfa8SLorenzo Pieralisi 1233e4dadfa8SLorenzo Pieralisi if (smmu->sync_gsiv) 1234e4dadfa8SLorenzo Pieralisi acpi_iort_register_irq(smmu->sync_gsiv, "cmdq-sync", 1235e4dadfa8SLorenzo Pieralisi ACPI_EDGE_SENSITIVE, 1236e4dadfa8SLorenzo Pieralisi &res[num_res++]); 1237e4dadfa8SLorenzo Pieralisi } 1238f935448aSGeetha Sowjanya } 1239e4dadfa8SLorenzo Pieralisi 124024e51604SNeil Leeder static void __init arm_smmu_v3_dma_configure(struct device *dev, 124124e51604SNeil Leeder struct acpi_iort_node *node) 1242e4dadfa8SLorenzo Pieralisi { 1243e4dadfa8SLorenzo Pieralisi struct acpi_iort_smmu_v3 *smmu; 124424e51604SNeil Leeder enum dev_dma_attr attr; 1245e4dadfa8SLorenzo Pieralisi 1246e4dadfa8SLorenzo Pieralisi /* Retrieve SMMUv3 specific data */ 1247e4dadfa8SLorenzo Pieralisi smmu = (struct acpi_iort_smmu_v3 *)node->node_data; 1248e4dadfa8SLorenzo Pieralisi 124924e51604SNeil Leeder attr = (smmu->flags & ACPI_IORT_SMMU_V3_COHACC_OVERRIDE) ? 125024e51604SNeil Leeder DEV_DMA_COHERENT : DEV_DMA_NON_COHERENT; 125124e51604SNeil Leeder 125224e51604SNeil Leeder /* We expect the dma masks to be equivalent for all SMMUv3 set-ups */ 125324e51604SNeil Leeder dev->dma_mask = &dev->coherent_dma_mask; 125424e51604SNeil Leeder 125524e51604SNeil Leeder /* Configure DMA for the page table walker */ 125624e51604SNeil Leeder acpi_dma_configure(dev, attr); 1257e4dadfa8SLorenzo Pieralisi } 1258e4dadfa8SLorenzo Pieralisi 125975808131SLorenzo Pieralisi #if defined(CONFIG_ACPI_NUMA) 12605fe0ce3bSGanapatrao Kulkarni /* 12615fe0ce3bSGanapatrao Kulkarni * set numa proximity domain for smmuv3 device 12625fe0ce3bSGanapatrao Kulkarni */ 126336a2ba07SKefeng Wang static int __init arm_smmu_v3_set_proximity(struct device *dev, 12645fe0ce3bSGanapatrao Kulkarni struct acpi_iort_node *node) 12655fe0ce3bSGanapatrao Kulkarni { 12665fe0ce3bSGanapatrao Kulkarni struct acpi_iort_smmu_v3 *smmu; 12675fe0ce3bSGanapatrao Kulkarni 12685fe0ce3bSGanapatrao Kulkarni smmu = (struct acpi_iort_smmu_v3 *)node->node_data; 12695fe0ce3bSGanapatrao Kulkarni if (smmu->flags & ACPI_IORT_SMMU_V3_PXM_VALID) { 12703e77eeb7SLorenzo Pieralisi int dev_node = acpi_map_pxm_to_node(smmu->pxm); 127136a2ba07SKefeng Wang 12723e77eeb7SLorenzo Pieralisi if (dev_node != NUMA_NO_NODE && !node_online(dev_node)) 127336a2ba07SKefeng Wang return -EINVAL; 127436a2ba07SKefeng Wang 12753e77eeb7SLorenzo Pieralisi set_dev_node(dev, dev_node); 12765fe0ce3bSGanapatrao Kulkarni pr_info("SMMU-v3[%llx] Mapped to Proximity domain %d\n", 12775fe0ce3bSGanapatrao Kulkarni smmu->base_address, 12785fe0ce3bSGanapatrao Kulkarni smmu->pxm); 12795fe0ce3bSGanapatrao Kulkarni } 128036a2ba07SKefeng Wang return 0; 12815fe0ce3bSGanapatrao Kulkarni } 12825fe0ce3bSGanapatrao Kulkarni #else 12835fe0ce3bSGanapatrao Kulkarni #define arm_smmu_v3_set_proximity NULL 12845fe0ce3bSGanapatrao Kulkarni #endif 12855fe0ce3bSGanapatrao Kulkarni 1286d6fcd3b1SLorenzo Pieralisi static int __init arm_smmu_count_resources(struct acpi_iort_node *node) 1287d6fcd3b1SLorenzo Pieralisi { 1288d6fcd3b1SLorenzo Pieralisi struct acpi_iort_smmu *smmu; 1289d6fcd3b1SLorenzo Pieralisi 1290d6fcd3b1SLorenzo Pieralisi /* Retrieve SMMU specific data */ 1291d6fcd3b1SLorenzo Pieralisi smmu = (struct acpi_iort_smmu *)node->node_data; 1292d6fcd3b1SLorenzo Pieralisi 1293d6fcd3b1SLorenzo Pieralisi /* 1294d6fcd3b1SLorenzo Pieralisi * Only consider the global fault interrupt and ignore the 1295d6fcd3b1SLorenzo Pieralisi * configuration access interrupt. 1296d6fcd3b1SLorenzo Pieralisi * 1297d6fcd3b1SLorenzo Pieralisi * MMIO address and global fault interrupt resources are always 1298d6fcd3b1SLorenzo Pieralisi * present so add them to the context interrupt count as a static 1299d6fcd3b1SLorenzo Pieralisi * value. 1300d6fcd3b1SLorenzo Pieralisi */ 1301d6fcd3b1SLorenzo Pieralisi return smmu->context_interrupt_count + 2; 1302d6fcd3b1SLorenzo Pieralisi } 1303d6fcd3b1SLorenzo Pieralisi 1304d6fcd3b1SLorenzo Pieralisi static void __init arm_smmu_init_resources(struct resource *res, 1305d6fcd3b1SLorenzo Pieralisi struct acpi_iort_node *node) 1306d6fcd3b1SLorenzo Pieralisi { 1307d6fcd3b1SLorenzo Pieralisi struct acpi_iort_smmu *smmu; 1308d6fcd3b1SLorenzo Pieralisi int i, hw_irq, trigger, num_res = 0; 1309d6fcd3b1SLorenzo Pieralisi u64 *ctx_irq, *glb_irq; 1310d6fcd3b1SLorenzo Pieralisi 1311d6fcd3b1SLorenzo Pieralisi /* Retrieve SMMU specific data */ 1312d6fcd3b1SLorenzo Pieralisi smmu = (struct acpi_iort_smmu *)node->node_data; 1313d6fcd3b1SLorenzo Pieralisi 1314d6fcd3b1SLorenzo Pieralisi res[num_res].start = smmu->base_address; 1315d6fcd3b1SLorenzo Pieralisi res[num_res].end = smmu->base_address + smmu->span - 1; 1316d6fcd3b1SLorenzo Pieralisi res[num_res].flags = IORESOURCE_MEM; 1317d6fcd3b1SLorenzo Pieralisi num_res++; 1318d6fcd3b1SLorenzo Pieralisi 1319d6fcd3b1SLorenzo Pieralisi glb_irq = ACPI_ADD_PTR(u64, node, smmu->global_interrupt_offset); 1320d6fcd3b1SLorenzo Pieralisi /* Global IRQs */ 1321d6fcd3b1SLorenzo Pieralisi hw_irq = IORT_IRQ_MASK(glb_irq[0]); 1322d6fcd3b1SLorenzo Pieralisi trigger = IORT_IRQ_TRIGGER_MASK(glb_irq[0]); 1323d6fcd3b1SLorenzo Pieralisi 1324d6fcd3b1SLorenzo Pieralisi acpi_iort_register_irq(hw_irq, "arm-smmu-global", trigger, 1325d6fcd3b1SLorenzo Pieralisi &res[num_res++]); 1326d6fcd3b1SLorenzo Pieralisi 1327d6fcd3b1SLorenzo Pieralisi /* Context IRQs */ 1328d6fcd3b1SLorenzo Pieralisi ctx_irq = ACPI_ADD_PTR(u64, node, smmu->context_interrupt_offset); 1329d6fcd3b1SLorenzo Pieralisi for (i = 0; i < smmu->context_interrupt_count; i++) { 1330d6fcd3b1SLorenzo Pieralisi hw_irq = IORT_IRQ_MASK(ctx_irq[i]); 1331d6fcd3b1SLorenzo Pieralisi trigger = IORT_IRQ_TRIGGER_MASK(ctx_irq[i]); 1332d6fcd3b1SLorenzo Pieralisi 1333d6fcd3b1SLorenzo Pieralisi acpi_iort_register_irq(hw_irq, "arm-smmu-context", trigger, 1334d6fcd3b1SLorenzo Pieralisi &res[num_res++]); 1335d6fcd3b1SLorenzo Pieralisi } 1336d6fcd3b1SLorenzo Pieralisi } 1337d6fcd3b1SLorenzo Pieralisi 133824e51604SNeil Leeder static void __init arm_smmu_dma_configure(struct device *dev, 133924e51604SNeil Leeder struct acpi_iort_node *node) 1340d6fcd3b1SLorenzo Pieralisi { 1341d6fcd3b1SLorenzo Pieralisi struct acpi_iort_smmu *smmu; 134224e51604SNeil Leeder enum dev_dma_attr attr; 1343d6fcd3b1SLorenzo Pieralisi 1344d6fcd3b1SLorenzo Pieralisi /* Retrieve SMMU specific data */ 1345d6fcd3b1SLorenzo Pieralisi smmu = (struct acpi_iort_smmu *)node->node_data; 1346d6fcd3b1SLorenzo Pieralisi 134724e51604SNeil Leeder attr = (smmu->flags & ACPI_IORT_SMMU_COHERENT_WALK) ? 134824e51604SNeil Leeder DEV_DMA_COHERENT : DEV_DMA_NON_COHERENT; 134924e51604SNeil Leeder 135024e51604SNeil Leeder /* We expect the dma masks to be equivalent for SMMU set-ups */ 135124e51604SNeil Leeder dev->dma_mask = &dev->coherent_dma_mask; 135224e51604SNeil Leeder 135324e51604SNeil Leeder /* Configure DMA for the page table walker */ 135424e51604SNeil Leeder acpi_dma_configure(dev, attr); 135524e51604SNeil Leeder } 135624e51604SNeil Leeder 135724e51604SNeil Leeder static int __init arm_smmu_v3_pmcg_count_resources(struct acpi_iort_node *node) 135824e51604SNeil Leeder { 135924e51604SNeil Leeder struct acpi_iort_pmcg *pmcg; 136024e51604SNeil Leeder 136124e51604SNeil Leeder /* Retrieve PMCG specific data */ 136224e51604SNeil Leeder pmcg = (struct acpi_iort_pmcg *)node->node_data; 136324e51604SNeil Leeder 136424e51604SNeil Leeder /* 136524e51604SNeil Leeder * There are always 2 memory resources. 136624e51604SNeil Leeder * If the overflow_gsiv is present then add that for a total of 3. 136724e51604SNeil Leeder */ 136824e51604SNeil Leeder return pmcg->overflow_gsiv ? 3 : 2; 136924e51604SNeil Leeder } 137024e51604SNeil Leeder 137124e51604SNeil Leeder static void __init arm_smmu_v3_pmcg_init_resources(struct resource *res, 137224e51604SNeil Leeder struct acpi_iort_node *node) 137324e51604SNeil Leeder { 137424e51604SNeil Leeder struct acpi_iort_pmcg *pmcg; 137524e51604SNeil Leeder 137624e51604SNeil Leeder /* Retrieve PMCG specific data */ 137724e51604SNeil Leeder pmcg = (struct acpi_iort_pmcg *)node->node_data; 137824e51604SNeil Leeder 137924e51604SNeil Leeder res[0].start = pmcg->page0_base_address; 138024e51604SNeil Leeder res[0].end = pmcg->page0_base_address + SZ_4K - 1; 138124e51604SNeil Leeder res[0].flags = IORESOURCE_MEM; 138224e51604SNeil Leeder res[1].start = pmcg->page1_base_address; 138324e51604SNeil Leeder res[1].end = pmcg->page1_base_address + SZ_4K - 1; 138424e51604SNeil Leeder res[1].flags = IORESOURCE_MEM; 138524e51604SNeil Leeder 138624e51604SNeil Leeder if (pmcg->overflow_gsiv) 138724e51604SNeil Leeder acpi_iort_register_irq(pmcg->overflow_gsiv, "overflow", 138824e51604SNeil Leeder ACPI_EDGE_SENSITIVE, &res[2]); 138924e51604SNeil Leeder } 139024e51604SNeil Leeder 139124062fe8SShameer Kolothum static struct acpi_platform_list pmcg_plat_info[] __initdata = { 139224062fe8SShameer Kolothum /* HiSilicon Hip08 Platform */ 139324062fe8SShameer Kolothum {"HISI ", "HIP08 ", 0, ACPI_SIG_IORT, greater_than_or_equal, 139424062fe8SShameer Kolothum "Erratum #162001800", IORT_SMMU_V3_PMCG_HISI_HIP08}, 139524062fe8SShameer Kolothum { } 139624062fe8SShameer Kolothum }; 139724062fe8SShameer Kolothum 139824e51604SNeil Leeder static int __init arm_smmu_v3_pmcg_add_platdata(struct platform_device *pdev) 139924e51604SNeil Leeder { 140024062fe8SShameer Kolothum u32 model; 140124062fe8SShameer Kolothum int idx; 140224062fe8SShameer Kolothum 140324062fe8SShameer Kolothum idx = acpi_match_platform_list(pmcg_plat_info); 140424062fe8SShameer Kolothum if (idx >= 0) 140524062fe8SShameer Kolothum model = pmcg_plat_info[idx].data; 140624062fe8SShameer Kolothum else 140724062fe8SShameer Kolothum model = IORT_SMMU_V3_PMCG_GENERIC; 140824e51604SNeil Leeder 140924e51604SNeil Leeder return platform_device_add_data(pdev, &model, sizeof(model)); 1410d6fcd3b1SLorenzo Pieralisi } 1411d6fcd3b1SLorenzo Pieralisi 1412896dd2c3SLorenzo Pieralisi struct iort_dev_config { 1413846f0e9eSLorenzo Pieralisi const char *name; 1414896dd2c3SLorenzo Pieralisi int (*dev_init)(struct acpi_iort_node *node); 141524e51604SNeil Leeder void (*dev_dma_configure)(struct device *dev, 141624e51604SNeil Leeder struct acpi_iort_node *node); 1417896dd2c3SLorenzo Pieralisi int (*dev_count_resources)(struct acpi_iort_node *node); 1418896dd2c3SLorenzo Pieralisi void (*dev_init_resources)(struct resource *res, 1419846f0e9eSLorenzo Pieralisi struct acpi_iort_node *node); 142036a2ba07SKefeng Wang int (*dev_set_proximity)(struct device *dev, 14215fe0ce3bSGanapatrao Kulkarni struct acpi_iort_node *node); 142224e51604SNeil Leeder int (*dev_add_platdata)(struct platform_device *pdev); 1423846f0e9eSLorenzo Pieralisi }; 1424846f0e9eSLorenzo Pieralisi 1425896dd2c3SLorenzo Pieralisi static const struct iort_dev_config iort_arm_smmu_v3_cfg __initconst = { 1426e4dadfa8SLorenzo Pieralisi .name = "arm-smmu-v3", 142724e51604SNeil Leeder .dev_dma_configure = arm_smmu_v3_dma_configure, 1428896dd2c3SLorenzo Pieralisi .dev_count_resources = arm_smmu_v3_count_resources, 1429896dd2c3SLorenzo Pieralisi .dev_init_resources = arm_smmu_v3_init_resources, 1430896dd2c3SLorenzo Pieralisi .dev_set_proximity = arm_smmu_v3_set_proximity, 1431e4dadfa8SLorenzo Pieralisi }; 1432e4dadfa8SLorenzo Pieralisi 1433896dd2c3SLorenzo Pieralisi static const struct iort_dev_config iort_arm_smmu_cfg __initconst = { 1434d6fcd3b1SLorenzo Pieralisi .name = "arm-smmu", 143524e51604SNeil Leeder .dev_dma_configure = arm_smmu_dma_configure, 1436896dd2c3SLorenzo Pieralisi .dev_count_resources = arm_smmu_count_resources, 143724e51604SNeil Leeder .dev_init_resources = arm_smmu_init_resources, 143824e51604SNeil Leeder }; 143924e51604SNeil Leeder 144024e51604SNeil Leeder static const struct iort_dev_config iort_arm_smmu_v3_pmcg_cfg __initconst = { 144124e51604SNeil Leeder .name = "arm-smmu-v3-pmcg", 144224e51604SNeil Leeder .dev_count_resources = arm_smmu_v3_pmcg_count_resources, 144324e51604SNeil Leeder .dev_init_resources = arm_smmu_v3_pmcg_init_resources, 144424e51604SNeil Leeder .dev_add_platdata = arm_smmu_v3_pmcg_add_platdata, 1445d6fcd3b1SLorenzo Pieralisi }; 1446d6fcd3b1SLorenzo Pieralisi 1447896dd2c3SLorenzo Pieralisi static __init const struct iort_dev_config *iort_get_dev_cfg( 1448e3d49392SLorenzo Pieralisi struct acpi_iort_node *node) 1449846f0e9eSLorenzo Pieralisi { 1450e4dadfa8SLorenzo Pieralisi switch (node->type) { 1451e4dadfa8SLorenzo Pieralisi case ACPI_IORT_NODE_SMMU_V3: 1452e4dadfa8SLorenzo Pieralisi return &iort_arm_smmu_v3_cfg; 1453d6fcd3b1SLorenzo Pieralisi case ACPI_IORT_NODE_SMMU: 1454d6fcd3b1SLorenzo Pieralisi return &iort_arm_smmu_cfg; 145524e51604SNeil Leeder case ACPI_IORT_NODE_PMCG: 145624e51604SNeil Leeder return &iort_arm_smmu_v3_pmcg_cfg; 1457e4dadfa8SLorenzo Pieralisi default: 1458846f0e9eSLorenzo Pieralisi return NULL; 1459846f0e9eSLorenzo Pieralisi } 1460e4dadfa8SLorenzo Pieralisi } 1461846f0e9eSLorenzo Pieralisi 1462846f0e9eSLorenzo Pieralisi /** 1463896dd2c3SLorenzo Pieralisi * iort_add_platform_device() - Allocate a platform device for IORT node 1464896dd2c3SLorenzo Pieralisi * @node: Pointer to device ACPI IORT node 1465846f0e9eSLorenzo Pieralisi * 1466846f0e9eSLorenzo Pieralisi * Returns: 0 on success, <0 failure 1467846f0e9eSLorenzo Pieralisi */ 1468896dd2c3SLorenzo Pieralisi static int __init iort_add_platform_device(struct acpi_iort_node *node, 1469896dd2c3SLorenzo Pieralisi const struct iort_dev_config *ops) 1470846f0e9eSLorenzo Pieralisi { 1471846f0e9eSLorenzo Pieralisi struct fwnode_handle *fwnode; 1472846f0e9eSLorenzo Pieralisi struct platform_device *pdev; 1473846f0e9eSLorenzo Pieralisi struct resource *r; 1474846f0e9eSLorenzo Pieralisi int ret, count; 1475846f0e9eSLorenzo Pieralisi 1476846f0e9eSLorenzo Pieralisi pdev = platform_device_alloc(ops->name, PLATFORM_DEVID_AUTO); 1477846f0e9eSLorenzo Pieralisi if (!pdev) 14785e5afa6cSDan Carpenter return -ENOMEM; 1479846f0e9eSLorenzo Pieralisi 148036a2ba07SKefeng Wang if (ops->dev_set_proximity) { 148136a2ba07SKefeng Wang ret = ops->dev_set_proximity(&pdev->dev, node); 148236a2ba07SKefeng Wang if (ret) 148336a2ba07SKefeng Wang goto dev_put; 148436a2ba07SKefeng Wang } 14855fe0ce3bSGanapatrao Kulkarni 1486896dd2c3SLorenzo Pieralisi count = ops->dev_count_resources(node); 1487846f0e9eSLorenzo Pieralisi 1488846f0e9eSLorenzo Pieralisi r = kcalloc(count, sizeof(*r), GFP_KERNEL); 1489846f0e9eSLorenzo Pieralisi if (!r) { 1490846f0e9eSLorenzo Pieralisi ret = -ENOMEM; 1491846f0e9eSLorenzo Pieralisi goto dev_put; 1492846f0e9eSLorenzo Pieralisi } 1493846f0e9eSLorenzo Pieralisi 1494896dd2c3SLorenzo Pieralisi ops->dev_init_resources(r, node); 1495846f0e9eSLorenzo Pieralisi 1496846f0e9eSLorenzo Pieralisi ret = platform_device_add_resources(pdev, r, count); 1497846f0e9eSLorenzo Pieralisi /* 1498846f0e9eSLorenzo Pieralisi * Resources are duplicated in platform_device_add_resources, 1499846f0e9eSLorenzo Pieralisi * free their allocated memory 1500846f0e9eSLorenzo Pieralisi */ 1501846f0e9eSLorenzo Pieralisi kfree(r); 1502846f0e9eSLorenzo Pieralisi 1503846f0e9eSLorenzo Pieralisi if (ret) 1504846f0e9eSLorenzo Pieralisi goto dev_put; 1505846f0e9eSLorenzo Pieralisi 1506846f0e9eSLorenzo Pieralisi /* 150724e51604SNeil Leeder * Platform devices based on PMCG nodes uses platform_data to 150824e51604SNeil Leeder * pass the hardware model info to the driver. For others, add 150924e51604SNeil Leeder * a copy of IORT node pointer to platform_data to be used to 151024e51604SNeil Leeder * retrieve IORT data information. 1511846f0e9eSLorenzo Pieralisi */ 151224e51604SNeil Leeder if (ops->dev_add_platdata) 151324e51604SNeil Leeder ret = ops->dev_add_platdata(pdev); 151424e51604SNeil Leeder else 1515846f0e9eSLorenzo Pieralisi ret = platform_device_add_data(pdev, &node, sizeof(node)); 151624e51604SNeil Leeder 1517846f0e9eSLorenzo Pieralisi if (ret) 1518846f0e9eSLorenzo Pieralisi goto dev_put; 1519846f0e9eSLorenzo Pieralisi 1520846f0e9eSLorenzo Pieralisi fwnode = iort_get_fwnode(node); 1521846f0e9eSLorenzo Pieralisi 1522846f0e9eSLorenzo Pieralisi if (!fwnode) { 1523846f0e9eSLorenzo Pieralisi ret = -ENODEV; 1524846f0e9eSLorenzo Pieralisi goto dev_put; 1525846f0e9eSLorenzo Pieralisi } 1526846f0e9eSLorenzo Pieralisi 1527846f0e9eSLorenzo Pieralisi pdev->dev.fwnode = fwnode; 1528846f0e9eSLorenzo Pieralisi 152924e51604SNeil Leeder if (ops->dev_dma_configure) 153024e51604SNeil Leeder ops->dev_dma_configure(&pdev->dev, node); 1531846f0e9eSLorenzo Pieralisi 153265637901SLorenzo Pieralisi iort_set_device_domain(&pdev->dev, node); 153365637901SLorenzo Pieralisi 1534846f0e9eSLorenzo Pieralisi ret = platform_device_add(pdev); 1535846f0e9eSLorenzo Pieralisi if (ret) 1536846f0e9eSLorenzo Pieralisi goto dma_deconfigure; 1537846f0e9eSLorenzo Pieralisi 1538846f0e9eSLorenzo Pieralisi return 0; 1539846f0e9eSLorenzo Pieralisi 1540846f0e9eSLorenzo Pieralisi dma_deconfigure: 1541dc3c0550SChristoph Hellwig arch_teardown_dma_ops(&pdev->dev); 1542846f0e9eSLorenzo Pieralisi dev_put: 1543846f0e9eSLorenzo Pieralisi platform_device_put(pdev); 1544846f0e9eSLorenzo Pieralisi 1545846f0e9eSLorenzo Pieralisi return ret; 1546846f0e9eSLorenzo Pieralisi } 1547846f0e9eSLorenzo Pieralisi 154843554cebSSinan Kaya #ifdef CONFIG_PCI 154943554cebSSinan Kaya static void __init iort_enable_acs(struct acpi_iort_node *iort_node) 155037f6b42eSLorenzo Pieralisi { 155143554cebSSinan Kaya static bool acs_enabled __initdata; 155243554cebSSinan Kaya 155343554cebSSinan Kaya if (acs_enabled) 155443554cebSSinan Kaya return; 155543554cebSSinan Kaya 155637f6b42eSLorenzo Pieralisi if (iort_node->type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX) { 155737f6b42eSLorenzo Pieralisi struct acpi_iort_node *parent; 155837f6b42eSLorenzo Pieralisi struct acpi_iort_id_mapping *map; 155937f6b42eSLorenzo Pieralisi int i; 156037f6b42eSLorenzo Pieralisi 156137f6b42eSLorenzo Pieralisi map = ACPI_ADD_PTR(struct acpi_iort_id_mapping, iort_node, 156237f6b42eSLorenzo Pieralisi iort_node->mapping_offset); 156337f6b42eSLorenzo Pieralisi 156437f6b42eSLorenzo Pieralisi for (i = 0; i < iort_node->mapping_count; i++, map++) { 156537f6b42eSLorenzo Pieralisi if (!map->output_reference) 156637f6b42eSLorenzo Pieralisi continue; 156737f6b42eSLorenzo Pieralisi 156837f6b42eSLorenzo Pieralisi parent = ACPI_ADD_PTR(struct acpi_iort_node, 156937f6b42eSLorenzo Pieralisi iort_table, map->output_reference); 157037f6b42eSLorenzo Pieralisi /* 157137f6b42eSLorenzo Pieralisi * If we detect a RC->SMMU mapping, make sure 157237f6b42eSLorenzo Pieralisi * we enable ACS on the system. 157337f6b42eSLorenzo Pieralisi */ 157437f6b42eSLorenzo Pieralisi if ((parent->type == ACPI_IORT_NODE_SMMU) || 157537f6b42eSLorenzo Pieralisi (parent->type == ACPI_IORT_NODE_SMMU_V3)) { 157637f6b42eSLorenzo Pieralisi pci_request_acs(); 157743554cebSSinan Kaya acs_enabled = true; 157843554cebSSinan Kaya return; 157937f6b42eSLorenzo Pieralisi } 158037f6b42eSLorenzo Pieralisi } 158137f6b42eSLorenzo Pieralisi } 158237f6b42eSLorenzo Pieralisi } 158343554cebSSinan Kaya #else 158443554cebSSinan Kaya static inline void iort_enable_acs(struct acpi_iort_node *iort_node) { } 158543554cebSSinan Kaya #endif 158637f6b42eSLorenzo Pieralisi 1587846f0e9eSLorenzo Pieralisi static void __init iort_init_platform_devices(void) 1588846f0e9eSLorenzo Pieralisi { 1589846f0e9eSLorenzo Pieralisi struct acpi_iort_node *iort_node, *iort_end; 1590846f0e9eSLorenzo Pieralisi struct acpi_table_iort *iort; 1591846f0e9eSLorenzo Pieralisi struct fwnode_handle *fwnode; 1592846f0e9eSLorenzo Pieralisi int i, ret; 1593896dd2c3SLorenzo Pieralisi const struct iort_dev_config *ops; 1594846f0e9eSLorenzo Pieralisi 1595846f0e9eSLorenzo Pieralisi /* 1596846f0e9eSLorenzo Pieralisi * iort_table and iort both point to the start of IORT table, but 1597846f0e9eSLorenzo Pieralisi * have different struct types 1598846f0e9eSLorenzo Pieralisi */ 1599846f0e9eSLorenzo Pieralisi iort = (struct acpi_table_iort *)iort_table; 1600846f0e9eSLorenzo Pieralisi 1601846f0e9eSLorenzo Pieralisi /* Get the first IORT node */ 1602846f0e9eSLorenzo Pieralisi iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort, 1603846f0e9eSLorenzo Pieralisi iort->node_offset); 1604846f0e9eSLorenzo Pieralisi iort_end = ACPI_ADD_PTR(struct acpi_iort_node, iort, 1605846f0e9eSLorenzo Pieralisi iort_table->length); 1606846f0e9eSLorenzo Pieralisi 1607846f0e9eSLorenzo Pieralisi for (i = 0; i < iort->node_count; i++) { 1608846f0e9eSLorenzo Pieralisi if (iort_node >= iort_end) { 1609846f0e9eSLorenzo Pieralisi pr_err("iort node pointer overflows, bad table\n"); 1610846f0e9eSLorenzo Pieralisi return; 1611846f0e9eSLorenzo Pieralisi } 1612846f0e9eSLorenzo Pieralisi 161343554cebSSinan Kaya iort_enable_acs(iort_node); 161437f6b42eSLorenzo Pieralisi 1615896dd2c3SLorenzo Pieralisi ops = iort_get_dev_cfg(iort_node); 1616896dd2c3SLorenzo Pieralisi if (ops) { 1617846f0e9eSLorenzo Pieralisi fwnode = acpi_alloc_fwnode_static(); 1618846f0e9eSLorenzo Pieralisi if (!fwnode) 1619846f0e9eSLorenzo Pieralisi return; 1620846f0e9eSLorenzo Pieralisi 1621846f0e9eSLorenzo Pieralisi iort_set_fwnode(iort_node, fwnode); 1622846f0e9eSLorenzo Pieralisi 1623896dd2c3SLorenzo Pieralisi ret = iort_add_platform_device(iort_node, ops); 1624846f0e9eSLorenzo Pieralisi if (ret) { 1625846f0e9eSLorenzo Pieralisi iort_delete_fwnode(iort_node); 1626846f0e9eSLorenzo Pieralisi acpi_free_fwnode_static(fwnode); 1627846f0e9eSLorenzo Pieralisi return; 1628846f0e9eSLorenzo Pieralisi } 1629846f0e9eSLorenzo Pieralisi } 1630846f0e9eSLorenzo Pieralisi 1631846f0e9eSLorenzo Pieralisi iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort_node, 1632846f0e9eSLorenzo Pieralisi iort_node->length); 1633846f0e9eSLorenzo Pieralisi } 1634846f0e9eSLorenzo Pieralisi } 1635846f0e9eSLorenzo Pieralisi 163688ef16d8STomasz Nowicki void __init acpi_iort_init(void) 163788ef16d8STomasz Nowicki { 163888ef16d8STomasz Nowicki acpi_status status; 163988ef16d8STomasz Nowicki 164088ef16d8STomasz Nowicki status = acpi_get_table(ACPI_SIG_IORT, 0, &iort_table); 164134ceea27SLorenzo Pieralisi if (ACPI_FAILURE(status)) { 164234ceea27SLorenzo Pieralisi if (status != AE_NOT_FOUND) { 164388ef16d8STomasz Nowicki const char *msg = acpi_format_exception(status); 164434ceea27SLorenzo Pieralisi 164588ef16d8STomasz Nowicki pr_err("Failed to get table, %s\n", msg); 164688ef16d8STomasz Nowicki } 164734ceea27SLorenzo Pieralisi 164834ceea27SLorenzo Pieralisi return; 164934ceea27SLorenzo Pieralisi } 165034ceea27SLorenzo Pieralisi 1651846f0e9eSLorenzo Pieralisi iort_init_platform_devices(); 165288ef16d8STomasz Nowicki } 1653