188ef16d8STomasz Nowicki /* 288ef16d8STomasz Nowicki * Copyright (C) 2016, Semihalf 388ef16d8STomasz Nowicki * Author: Tomasz Nowicki <tn@semihalf.com> 488ef16d8STomasz Nowicki * 588ef16d8STomasz Nowicki * This program is free software; you can redistribute it and/or modify it 688ef16d8STomasz Nowicki * under the terms and conditions of the GNU General Public License, 788ef16d8STomasz Nowicki * version 2, as published by the Free Software Foundation. 888ef16d8STomasz Nowicki * 988ef16d8STomasz Nowicki * This program is distributed in the hope it will be useful, but WITHOUT 1088ef16d8STomasz Nowicki * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 1188ef16d8STomasz Nowicki * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 1288ef16d8STomasz Nowicki * more details. 1388ef16d8STomasz Nowicki * 1488ef16d8STomasz Nowicki * This file implements early detection/parsing of I/O mapping 1588ef16d8STomasz Nowicki * reported to OS through firmware via I/O Remapping Table (IORT) 1688ef16d8STomasz Nowicki * IORT document number: ARM DEN 0049A 1788ef16d8STomasz Nowicki */ 1888ef16d8STomasz Nowicki 1988ef16d8STomasz Nowicki #define pr_fmt(fmt) "ACPI: IORT: " fmt 2088ef16d8STomasz Nowicki 2188ef16d8STomasz Nowicki #include <linux/acpi_iort.h> 22846f0e9eSLorenzo Pieralisi #include <linux/iommu.h> 2388ef16d8STomasz Nowicki #include <linux/kernel.h> 247936df92SLorenzo Pieralisi #include <linux/list.h> 2588ef16d8STomasz Nowicki #include <linux/pci.h> 26846f0e9eSLorenzo Pieralisi #include <linux/platform_device.h> 277936df92SLorenzo Pieralisi #include <linux/slab.h> 2888ef16d8STomasz Nowicki 29ea50b524SLorenzo Pieralisi #define IORT_TYPE_MASK(type) (1 << (type)) 30ea50b524SLorenzo Pieralisi #define IORT_MSI_TYPE (1 << ACPI_IORT_NODE_ITS_GROUP) 31643b8e4dSLorenzo Pieralisi #define IORT_IOMMU_TYPE ((1 << ACPI_IORT_NODE_SMMU) | \ 32643b8e4dSLorenzo Pieralisi (1 << ACPI_IORT_NODE_SMMU_V3)) 33ea50b524SLorenzo Pieralisi 3412275bf0SRobert Richter /* Until ACPICA headers cover IORT rev. C */ 3512275bf0SRobert Richter #ifndef ACPI_IORT_SMMU_V3_CAVIUM_CN99XX 3612275bf0SRobert Richter #define ACPI_IORT_SMMU_V3_CAVIUM_CN99XX 0x2 3712275bf0SRobert Richter #endif 3812275bf0SRobert Richter 394bf2efd2STomasz Nowicki struct iort_its_msi_chip { 404bf2efd2STomasz Nowicki struct list_head list; 414bf2efd2STomasz Nowicki struct fwnode_handle *fw_node; 42*8b4282e6SShameer Kolothum phys_addr_t base_addr; 434bf2efd2STomasz Nowicki u32 translation_id; 444bf2efd2STomasz Nowicki }; 454bf2efd2STomasz Nowicki 467936df92SLorenzo Pieralisi struct iort_fwnode { 477936df92SLorenzo Pieralisi struct list_head list; 487936df92SLorenzo Pieralisi struct acpi_iort_node *iort_node; 497936df92SLorenzo Pieralisi struct fwnode_handle *fwnode; 507936df92SLorenzo Pieralisi }; 517936df92SLorenzo Pieralisi static LIST_HEAD(iort_fwnode_list); 527936df92SLorenzo Pieralisi static DEFINE_SPINLOCK(iort_fwnode_lock); 537936df92SLorenzo Pieralisi 547936df92SLorenzo Pieralisi /** 557936df92SLorenzo Pieralisi * iort_set_fwnode() - Create iort_fwnode and use it to register 567936df92SLorenzo Pieralisi * iommu data in the iort_fwnode_list 577936df92SLorenzo Pieralisi * 587936df92SLorenzo Pieralisi * @node: IORT table node associated with the IOMMU 597936df92SLorenzo Pieralisi * @fwnode: fwnode associated with the IORT node 607936df92SLorenzo Pieralisi * 617936df92SLorenzo Pieralisi * Returns: 0 on success 627936df92SLorenzo Pieralisi * <0 on failure 637936df92SLorenzo Pieralisi */ 647936df92SLorenzo Pieralisi static inline int iort_set_fwnode(struct acpi_iort_node *iort_node, 657936df92SLorenzo Pieralisi struct fwnode_handle *fwnode) 667936df92SLorenzo Pieralisi { 677936df92SLorenzo Pieralisi struct iort_fwnode *np; 687936df92SLorenzo Pieralisi 697936df92SLorenzo Pieralisi np = kzalloc(sizeof(struct iort_fwnode), GFP_ATOMIC); 707936df92SLorenzo Pieralisi 717936df92SLorenzo Pieralisi if (WARN_ON(!np)) 727936df92SLorenzo Pieralisi return -ENOMEM; 737936df92SLorenzo Pieralisi 747936df92SLorenzo Pieralisi INIT_LIST_HEAD(&np->list); 757936df92SLorenzo Pieralisi np->iort_node = iort_node; 767936df92SLorenzo Pieralisi np->fwnode = fwnode; 777936df92SLorenzo Pieralisi 787936df92SLorenzo Pieralisi spin_lock(&iort_fwnode_lock); 797936df92SLorenzo Pieralisi list_add_tail(&np->list, &iort_fwnode_list); 807936df92SLorenzo Pieralisi spin_unlock(&iort_fwnode_lock); 817936df92SLorenzo Pieralisi 827936df92SLorenzo Pieralisi return 0; 837936df92SLorenzo Pieralisi } 847936df92SLorenzo Pieralisi 857936df92SLorenzo Pieralisi /** 867936df92SLorenzo Pieralisi * iort_get_fwnode() - Retrieve fwnode associated with an IORT node 877936df92SLorenzo Pieralisi * 887936df92SLorenzo Pieralisi * @node: IORT table node to be looked-up 897936df92SLorenzo Pieralisi * 907936df92SLorenzo Pieralisi * Returns: fwnode_handle pointer on success, NULL on failure 917936df92SLorenzo Pieralisi */ 92e3d49392SLorenzo Pieralisi static inline struct fwnode_handle *iort_get_fwnode( 93e3d49392SLorenzo Pieralisi struct acpi_iort_node *node) 947936df92SLorenzo Pieralisi { 957936df92SLorenzo Pieralisi struct iort_fwnode *curr; 967936df92SLorenzo Pieralisi struct fwnode_handle *fwnode = NULL; 977936df92SLorenzo Pieralisi 987936df92SLorenzo Pieralisi spin_lock(&iort_fwnode_lock); 997936df92SLorenzo Pieralisi list_for_each_entry(curr, &iort_fwnode_list, list) { 1007936df92SLorenzo Pieralisi if (curr->iort_node == node) { 1017936df92SLorenzo Pieralisi fwnode = curr->fwnode; 1027936df92SLorenzo Pieralisi break; 1037936df92SLorenzo Pieralisi } 1047936df92SLorenzo Pieralisi } 1057936df92SLorenzo Pieralisi spin_unlock(&iort_fwnode_lock); 1067936df92SLorenzo Pieralisi 1077936df92SLorenzo Pieralisi return fwnode; 1087936df92SLorenzo Pieralisi } 1097936df92SLorenzo Pieralisi 1107936df92SLorenzo Pieralisi /** 1117936df92SLorenzo Pieralisi * iort_delete_fwnode() - Delete fwnode associated with an IORT node 1127936df92SLorenzo Pieralisi * 1137936df92SLorenzo Pieralisi * @node: IORT table node associated with fwnode to delete 1147936df92SLorenzo Pieralisi */ 1157936df92SLorenzo Pieralisi static inline void iort_delete_fwnode(struct acpi_iort_node *node) 1167936df92SLorenzo Pieralisi { 1177936df92SLorenzo Pieralisi struct iort_fwnode *curr, *tmp; 1187936df92SLorenzo Pieralisi 1197936df92SLorenzo Pieralisi spin_lock(&iort_fwnode_lock); 1207936df92SLorenzo Pieralisi list_for_each_entry_safe(curr, tmp, &iort_fwnode_list, list) { 1217936df92SLorenzo Pieralisi if (curr->iort_node == node) { 1227936df92SLorenzo Pieralisi list_del(&curr->list); 1237936df92SLorenzo Pieralisi kfree(curr); 1247936df92SLorenzo Pieralisi break; 1257936df92SLorenzo Pieralisi } 1267936df92SLorenzo Pieralisi } 1277936df92SLorenzo Pieralisi spin_unlock(&iort_fwnode_lock); 1287936df92SLorenzo Pieralisi } 1297936df92SLorenzo Pieralisi 1300a71d8b9SHanjun Guo /** 1310a71d8b9SHanjun Guo * iort_get_iort_node() - Retrieve iort_node associated with an fwnode 1320a71d8b9SHanjun Guo * 1330a71d8b9SHanjun Guo * @fwnode: fwnode associated with device to be looked-up 1340a71d8b9SHanjun Guo * 1350a71d8b9SHanjun Guo * Returns: iort_node pointer on success, NULL on failure 1360a71d8b9SHanjun Guo */ 1370a71d8b9SHanjun Guo static inline struct acpi_iort_node *iort_get_iort_node( 1380a71d8b9SHanjun Guo struct fwnode_handle *fwnode) 1390a71d8b9SHanjun Guo { 1400a71d8b9SHanjun Guo struct iort_fwnode *curr; 1410a71d8b9SHanjun Guo struct acpi_iort_node *iort_node = NULL; 1420a71d8b9SHanjun Guo 1430a71d8b9SHanjun Guo spin_lock(&iort_fwnode_lock); 1440a71d8b9SHanjun Guo list_for_each_entry(curr, &iort_fwnode_list, list) { 1450a71d8b9SHanjun Guo if (curr->fwnode == fwnode) { 1460a71d8b9SHanjun Guo iort_node = curr->iort_node; 1470a71d8b9SHanjun Guo break; 1480a71d8b9SHanjun Guo } 1490a71d8b9SHanjun Guo } 1500a71d8b9SHanjun Guo spin_unlock(&iort_fwnode_lock); 1510a71d8b9SHanjun Guo 1520a71d8b9SHanjun Guo return iort_node; 1530a71d8b9SHanjun Guo } 1540a71d8b9SHanjun Guo 15588ef16d8STomasz Nowicki typedef acpi_status (*iort_find_node_callback) 15688ef16d8STomasz Nowicki (struct acpi_iort_node *node, void *context); 15788ef16d8STomasz Nowicki 15888ef16d8STomasz Nowicki /* Root pointer to the mapped IORT table */ 15988ef16d8STomasz Nowicki static struct acpi_table_header *iort_table; 16088ef16d8STomasz Nowicki 16188ef16d8STomasz Nowicki static LIST_HEAD(iort_msi_chip_list); 16288ef16d8STomasz Nowicki static DEFINE_SPINLOCK(iort_msi_chip_lock); 16388ef16d8STomasz Nowicki 1644bf2efd2STomasz Nowicki /** 165*8b4282e6SShameer Kolothum * iort_register_domain_token() - register domain token along with related 166*8b4282e6SShameer Kolothum * ITS ID and base address to the list from where we can get it back later on. 1674bf2efd2STomasz Nowicki * @trans_id: ITS ID. 168*8b4282e6SShameer Kolothum * @base: ITS base address. 1694bf2efd2STomasz Nowicki * @fw_node: Domain token. 1704bf2efd2STomasz Nowicki * 1714bf2efd2STomasz Nowicki * Returns: 0 on success, -ENOMEM if no memory when allocating list element 1724bf2efd2STomasz Nowicki */ 173*8b4282e6SShameer Kolothum int iort_register_domain_token(int trans_id, phys_addr_t base, 174*8b4282e6SShameer Kolothum struct fwnode_handle *fw_node) 1754bf2efd2STomasz Nowicki { 1764bf2efd2STomasz Nowicki struct iort_its_msi_chip *its_msi_chip; 1774bf2efd2STomasz Nowicki 1784bf2efd2STomasz Nowicki its_msi_chip = kzalloc(sizeof(*its_msi_chip), GFP_KERNEL); 1794bf2efd2STomasz Nowicki if (!its_msi_chip) 1804bf2efd2STomasz Nowicki return -ENOMEM; 1814bf2efd2STomasz Nowicki 1824bf2efd2STomasz Nowicki its_msi_chip->fw_node = fw_node; 1834bf2efd2STomasz Nowicki its_msi_chip->translation_id = trans_id; 184*8b4282e6SShameer Kolothum its_msi_chip->base_addr = base; 1854bf2efd2STomasz Nowicki 1864bf2efd2STomasz Nowicki spin_lock(&iort_msi_chip_lock); 1874bf2efd2STomasz Nowicki list_add(&its_msi_chip->list, &iort_msi_chip_list); 1884bf2efd2STomasz Nowicki spin_unlock(&iort_msi_chip_lock); 1894bf2efd2STomasz Nowicki 1904bf2efd2STomasz Nowicki return 0; 1914bf2efd2STomasz Nowicki } 1924bf2efd2STomasz Nowicki 1934bf2efd2STomasz Nowicki /** 1944bf2efd2STomasz Nowicki * iort_deregister_domain_token() - Deregister domain token based on ITS ID 1954bf2efd2STomasz Nowicki * @trans_id: ITS ID. 1964bf2efd2STomasz Nowicki * 1974bf2efd2STomasz Nowicki * Returns: none. 1984bf2efd2STomasz Nowicki */ 1994bf2efd2STomasz Nowicki void iort_deregister_domain_token(int trans_id) 2004bf2efd2STomasz Nowicki { 2014bf2efd2STomasz Nowicki struct iort_its_msi_chip *its_msi_chip, *t; 2024bf2efd2STomasz Nowicki 2034bf2efd2STomasz Nowicki spin_lock(&iort_msi_chip_lock); 2044bf2efd2STomasz Nowicki list_for_each_entry_safe(its_msi_chip, t, &iort_msi_chip_list, list) { 2054bf2efd2STomasz Nowicki if (its_msi_chip->translation_id == trans_id) { 2064bf2efd2STomasz Nowicki list_del(&its_msi_chip->list); 2074bf2efd2STomasz Nowicki kfree(its_msi_chip); 2084bf2efd2STomasz Nowicki break; 2094bf2efd2STomasz Nowicki } 2104bf2efd2STomasz Nowicki } 2114bf2efd2STomasz Nowicki spin_unlock(&iort_msi_chip_lock); 2124bf2efd2STomasz Nowicki } 2134bf2efd2STomasz Nowicki 2144bf2efd2STomasz Nowicki /** 2154bf2efd2STomasz Nowicki * iort_find_domain_token() - Find domain token based on given ITS ID 2164bf2efd2STomasz Nowicki * @trans_id: ITS ID. 2174bf2efd2STomasz Nowicki * 2184bf2efd2STomasz Nowicki * Returns: domain token when find on the list, NULL otherwise 2194bf2efd2STomasz Nowicki */ 2204bf2efd2STomasz Nowicki struct fwnode_handle *iort_find_domain_token(int trans_id) 2214bf2efd2STomasz Nowicki { 2224bf2efd2STomasz Nowicki struct fwnode_handle *fw_node = NULL; 2234bf2efd2STomasz Nowicki struct iort_its_msi_chip *its_msi_chip; 2244bf2efd2STomasz Nowicki 2254bf2efd2STomasz Nowicki spin_lock(&iort_msi_chip_lock); 2264bf2efd2STomasz Nowicki list_for_each_entry(its_msi_chip, &iort_msi_chip_list, list) { 2274bf2efd2STomasz Nowicki if (its_msi_chip->translation_id == trans_id) { 2284bf2efd2STomasz Nowicki fw_node = its_msi_chip->fw_node; 2294bf2efd2STomasz Nowicki break; 2304bf2efd2STomasz Nowicki } 2314bf2efd2STomasz Nowicki } 2324bf2efd2STomasz Nowicki spin_unlock(&iort_msi_chip_lock); 2334bf2efd2STomasz Nowicki 2344bf2efd2STomasz Nowicki return fw_node; 2354bf2efd2STomasz Nowicki } 2364bf2efd2STomasz Nowicki 23788ef16d8STomasz Nowicki static struct acpi_iort_node *iort_scan_node(enum acpi_iort_node_type type, 23888ef16d8STomasz Nowicki iort_find_node_callback callback, 23988ef16d8STomasz Nowicki void *context) 24088ef16d8STomasz Nowicki { 24188ef16d8STomasz Nowicki struct acpi_iort_node *iort_node, *iort_end; 24288ef16d8STomasz Nowicki struct acpi_table_iort *iort; 24388ef16d8STomasz Nowicki int i; 24488ef16d8STomasz Nowicki 24588ef16d8STomasz Nowicki if (!iort_table) 24688ef16d8STomasz Nowicki return NULL; 24788ef16d8STomasz Nowicki 24888ef16d8STomasz Nowicki /* Get the first IORT node */ 24988ef16d8STomasz Nowicki iort = (struct acpi_table_iort *)iort_table; 25088ef16d8STomasz Nowicki iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort, 25188ef16d8STomasz Nowicki iort->node_offset); 25288ef16d8STomasz Nowicki iort_end = ACPI_ADD_PTR(struct acpi_iort_node, iort_table, 25388ef16d8STomasz Nowicki iort_table->length); 25488ef16d8STomasz Nowicki 25588ef16d8STomasz Nowicki for (i = 0; i < iort->node_count; i++) { 25688ef16d8STomasz Nowicki if (WARN_TAINT(iort_node >= iort_end, TAINT_FIRMWARE_WORKAROUND, 25788ef16d8STomasz Nowicki "IORT node pointer overflows, bad table!\n")) 25888ef16d8STomasz Nowicki return NULL; 25988ef16d8STomasz Nowicki 26088ef16d8STomasz Nowicki if (iort_node->type == type && 26188ef16d8STomasz Nowicki ACPI_SUCCESS(callback(iort_node, context))) 26288ef16d8STomasz Nowicki return iort_node; 26388ef16d8STomasz Nowicki 26488ef16d8STomasz Nowicki iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort_node, 26588ef16d8STomasz Nowicki iort_node->length); 26688ef16d8STomasz Nowicki } 26788ef16d8STomasz Nowicki 26888ef16d8STomasz Nowicki return NULL; 26988ef16d8STomasz Nowicki } 27088ef16d8STomasz Nowicki 27188ef16d8STomasz Nowicki static acpi_status iort_match_node_callback(struct acpi_iort_node *node, 27288ef16d8STomasz Nowicki void *context) 27388ef16d8STomasz Nowicki { 27488ef16d8STomasz Nowicki struct device *dev = context; 275c92bdfe8SHanjun Guo acpi_status status = AE_NOT_FOUND; 27688ef16d8STomasz Nowicki 27788ef16d8STomasz Nowicki if (node->type == ACPI_IORT_NODE_NAMED_COMPONENT) { 27888ef16d8STomasz Nowicki struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL }; 27988ef16d8STomasz Nowicki struct acpi_device *adev = to_acpi_device_node(dev->fwnode); 28088ef16d8STomasz Nowicki struct acpi_iort_named_component *ncomp; 28188ef16d8STomasz Nowicki 282c92bdfe8SHanjun Guo if (!adev) 28388ef16d8STomasz Nowicki goto out; 28488ef16d8STomasz Nowicki 28588ef16d8STomasz Nowicki status = acpi_get_name(adev->handle, ACPI_FULL_PATHNAME, &buf); 28688ef16d8STomasz Nowicki if (ACPI_FAILURE(status)) { 28788ef16d8STomasz Nowicki dev_warn(dev, "Can't get device full path name\n"); 28888ef16d8STomasz Nowicki goto out; 28988ef16d8STomasz Nowicki } 29088ef16d8STomasz Nowicki 29188ef16d8STomasz Nowicki ncomp = (struct acpi_iort_named_component *)node->node_data; 29288ef16d8STomasz Nowicki status = !strcmp(ncomp->device_name, buf.pointer) ? 29388ef16d8STomasz Nowicki AE_OK : AE_NOT_FOUND; 29488ef16d8STomasz Nowicki acpi_os_free(buf.pointer); 29588ef16d8STomasz Nowicki } else if (node->type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX) { 29688ef16d8STomasz Nowicki struct acpi_iort_root_complex *pci_rc; 29788ef16d8STomasz Nowicki struct pci_bus *bus; 29888ef16d8STomasz Nowicki 29988ef16d8STomasz Nowicki bus = to_pci_bus(dev); 30088ef16d8STomasz Nowicki pci_rc = (struct acpi_iort_root_complex *)node->node_data; 30188ef16d8STomasz Nowicki 30288ef16d8STomasz Nowicki /* 30388ef16d8STomasz Nowicki * It is assumed that PCI segment numbers maps one-to-one 30488ef16d8STomasz Nowicki * with root complexes. Each segment number can represent only 30588ef16d8STomasz Nowicki * one root complex. 30688ef16d8STomasz Nowicki */ 30788ef16d8STomasz Nowicki status = pci_rc->pci_segment_number == pci_domain_nr(bus) ? 30888ef16d8STomasz Nowicki AE_OK : AE_NOT_FOUND; 30988ef16d8STomasz Nowicki } 31088ef16d8STomasz Nowicki out: 31188ef16d8STomasz Nowicki return status; 31288ef16d8STomasz Nowicki } 31388ef16d8STomasz Nowicki 31488ef16d8STomasz Nowicki static int iort_id_map(struct acpi_iort_id_mapping *map, u8 type, u32 rid_in, 31588ef16d8STomasz Nowicki u32 *rid_out) 31688ef16d8STomasz Nowicki { 31788ef16d8STomasz Nowicki /* Single mapping does not care for input id */ 31888ef16d8STomasz Nowicki if (map->flags & ACPI_IORT_ID_SINGLE_MAPPING) { 31988ef16d8STomasz Nowicki if (type == ACPI_IORT_NODE_NAMED_COMPONENT || 32088ef16d8STomasz Nowicki type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX) { 32188ef16d8STomasz Nowicki *rid_out = map->output_base; 32288ef16d8STomasz Nowicki return 0; 32388ef16d8STomasz Nowicki } 32488ef16d8STomasz Nowicki 32588ef16d8STomasz Nowicki pr_warn(FW_BUG "[map %p] SINGLE MAPPING flag not allowed for node type %d, skipping ID map\n", 32688ef16d8STomasz Nowicki map, type); 32788ef16d8STomasz Nowicki return -ENXIO; 32888ef16d8STomasz Nowicki } 32988ef16d8STomasz Nowicki 33088ef16d8STomasz Nowicki if (rid_in < map->input_base || 33188ef16d8STomasz Nowicki (rid_in >= map->input_base + map->id_count)) 33288ef16d8STomasz Nowicki return -ENXIO; 33388ef16d8STomasz Nowicki 33488ef16d8STomasz Nowicki *rid_out = map->output_base + (rid_in - map->input_base); 33588ef16d8STomasz Nowicki return 0; 33688ef16d8STomasz Nowicki } 33788ef16d8STomasz Nowicki 338e3d49392SLorenzo Pieralisi static struct acpi_iort_node *iort_node_get_id(struct acpi_iort_node *node, 3398ca4f1d3SHanjun Guo u32 *id_out, int index) 340618f535aSLorenzo Pieralisi { 341618f535aSLorenzo Pieralisi struct acpi_iort_node *parent; 342618f535aSLorenzo Pieralisi struct acpi_iort_id_mapping *map; 343618f535aSLorenzo Pieralisi 344618f535aSLorenzo Pieralisi if (!node->mapping_offset || !node->mapping_count || 345618f535aSLorenzo Pieralisi index >= node->mapping_count) 346618f535aSLorenzo Pieralisi return NULL; 347618f535aSLorenzo Pieralisi 348618f535aSLorenzo Pieralisi map = ACPI_ADD_PTR(struct acpi_iort_id_mapping, node, 349030abd8aSLorenzo Pieralisi node->mapping_offset + index * sizeof(*map)); 350618f535aSLorenzo Pieralisi 351618f535aSLorenzo Pieralisi /* Firmware bug! */ 352618f535aSLorenzo Pieralisi if (!map->output_reference) { 353618f535aSLorenzo Pieralisi pr_err(FW_BUG "[node %p type %d] ID map has NULL parent reference\n", 354618f535aSLorenzo Pieralisi node, node->type); 355618f535aSLorenzo Pieralisi return NULL; 356618f535aSLorenzo Pieralisi } 357618f535aSLorenzo Pieralisi 358618f535aSLorenzo Pieralisi parent = ACPI_ADD_PTR(struct acpi_iort_node, iort_table, 359618f535aSLorenzo Pieralisi map->output_reference); 360618f535aSLorenzo Pieralisi 361030abd8aSLorenzo Pieralisi if (map->flags & ACPI_IORT_ID_SINGLE_MAPPING) { 362618f535aSLorenzo Pieralisi if (node->type == ACPI_IORT_NODE_NAMED_COMPONENT || 36386456a3fSHanjun Guo node->type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX || 36486456a3fSHanjun Guo node->type == ACPI_IORT_NODE_SMMU_V3) { 365030abd8aSLorenzo Pieralisi *id_out = map->output_base; 366618f535aSLorenzo Pieralisi return parent; 367618f535aSLorenzo Pieralisi } 368618f535aSLorenzo Pieralisi } 369618f535aSLorenzo Pieralisi 370618f535aSLorenzo Pieralisi return NULL; 371618f535aSLorenzo Pieralisi } 372618f535aSLorenzo Pieralisi 37386456a3fSHanjun Guo #if (ACPI_CA_VERSION > 0x20170929) 37486456a3fSHanjun Guo static int iort_get_id_mapping_index(struct acpi_iort_node *node) 37586456a3fSHanjun Guo { 37686456a3fSHanjun Guo struct acpi_iort_smmu_v3 *smmu; 37786456a3fSHanjun Guo 37886456a3fSHanjun Guo switch (node->type) { 37986456a3fSHanjun Guo case ACPI_IORT_NODE_SMMU_V3: 38086456a3fSHanjun Guo /* 38186456a3fSHanjun Guo * SMMUv3 dev ID mapping index was introduced in revision 1 38286456a3fSHanjun Guo * table, not available in revision 0 38386456a3fSHanjun Guo */ 38486456a3fSHanjun Guo if (node->revision < 1) 38586456a3fSHanjun Guo return -EINVAL; 38686456a3fSHanjun Guo 38786456a3fSHanjun Guo smmu = (struct acpi_iort_smmu_v3 *)node->node_data; 38886456a3fSHanjun Guo /* 38986456a3fSHanjun Guo * ID mapping index is only ignored if all interrupts are 39086456a3fSHanjun Guo * GSIV based 39186456a3fSHanjun Guo */ 39286456a3fSHanjun Guo if (smmu->event_gsiv && smmu->pri_gsiv && smmu->gerr_gsiv 39386456a3fSHanjun Guo && smmu->sync_gsiv) 39486456a3fSHanjun Guo return -EINVAL; 39586456a3fSHanjun Guo 39686456a3fSHanjun Guo if (smmu->id_mapping_index >= node->mapping_count) { 39786456a3fSHanjun Guo pr_err(FW_BUG "[node %p type %d] ID mapping index overflows valid mappings\n", 39886456a3fSHanjun Guo node, node->type); 39986456a3fSHanjun Guo return -EINVAL; 40086456a3fSHanjun Guo } 40186456a3fSHanjun Guo 40286456a3fSHanjun Guo return smmu->id_mapping_index; 40386456a3fSHanjun Guo default: 40486456a3fSHanjun Guo return -EINVAL; 40586456a3fSHanjun Guo } 40686456a3fSHanjun Guo } 40786456a3fSHanjun Guo #else 4088c8df8dcSHanjun Guo static inline int iort_get_id_mapping_index(struct acpi_iort_node *node) 4098c8df8dcSHanjun Guo { 4108c8df8dcSHanjun Guo return -EINVAL; 4118c8df8dcSHanjun Guo } 41286456a3fSHanjun Guo #endif 4138c8df8dcSHanjun Guo 414697f6093SHanjun Guo static struct acpi_iort_node *iort_node_map_id(struct acpi_iort_node *node, 415697f6093SHanjun Guo u32 id_in, u32 *id_out, 416ea50b524SLorenzo Pieralisi u8 type_mask) 41788ef16d8STomasz Nowicki { 418697f6093SHanjun Guo u32 id = id_in; 41988ef16d8STomasz Nowicki 42088ef16d8STomasz Nowicki /* Parse the ID mapping tree to find specified node type */ 42188ef16d8STomasz Nowicki while (node) { 42288ef16d8STomasz Nowicki struct acpi_iort_id_mapping *map; 4238c8df8dcSHanjun Guo int i, index; 42488ef16d8STomasz Nowicki 425ea50b524SLorenzo Pieralisi if (IORT_TYPE_MASK(node->type) & type_mask) { 426697f6093SHanjun Guo if (id_out) 427697f6093SHanjun Guo *id_out = id; 42888ef16d8STomasz Nowicki return node; 42988ef16d8STomasz Nowicki } 43088ef16d8STomasz Nowicki 43188ef16d8STomasz Nowicki if (!node->mapping_offset || !node->mapping_count) 43288ef16d8STomasz Nowicki goto fail_map; 43388ef16d8STomasz Nowicki 43488ef16d8STomasz Nowicki map = ACPI_ADD_PTR(struct acpi_iort_id_mapping, node, 43588ef16d8STomasz Nowicki node->mapping_offset); 43688ef16d8STomasz Nowicki 43788ef16d8STomasz Nowicki /* Firmware bug! */ 43888ef16d8STomasz Nowicki if (!map->output_reference) { 43988ef16d8STomasz Nowicki pr_err(FW_BUG "[node %p type %d] ID map has NULL parent reference\n", 44088ef16d8STomasz Nowicki node, node->type); 44188ef16d8STomasz Nowicki goto fail_map; 44288ef16d8STomasz Nowicki } 44388ef16d8STomasz Nowicki 4448c8df8dcSHanjun Guo /* 4458c8df8dcSHanjun Guo * Get the special ID mapping index (if any) and skip its 4468c8df8dcSHanjun Guo * associated ID map to prevent erroneous multi-stage 4478c8df8dcSHanjun Guo * IORT ID translations. 4488c8df8dcSHanjun Guo */ 4498c8df8dcSHanjun Guo index = iort_get_id_mapping_index(node); 4508c8df8dcSHanjun Guo 451697f6093SHanjun Guo /* Do the ID translation */ 45288ef16d8STomasz Nowicki for (i = 0; i < node->mapping_count; i++, map++) { 4538c8df8dcSHanjun Guo /* if it is special mapping index, skip it */ 4548c8df8dcSHanjun Guo if (i == index) 4558c8df8dcSHanjun Guo continue; 4568c8df8dcSHanjun Guo 457697f6093SHanjun Guo if (!iort_id_map(map, node->type, id, &id)) 45888ef16d8STomasz Nowicki break; 45988ef16d8STomasz Nowicki } 46088ef16d8STomasz Nowicki 46188ef16d8STomasz Nowicki if (i == node->mapping_count) 46288ef16d8STomasz Nowicki goto fail_map; 46388ef16d8STomasz Nowicki 46488ef16d8STomasz Nowicki node = ACPI_ADD_PTR(struct acpi_iort_node, iort_table, 46588ef16d8STomasz Nowicki map->output_reference); 46688ef16d8STomasz Nowicki } 46788ef16d8STomasz Nowicki 46888ef16d8STomasz Nowicki fail_map: 469697f6093SHanjun Guo /* Map input ID to output ID unchanged on mapping failure */ 470697f6093SHanjun Guo if (id_out) 471697f6093SHanjun Guo *id_out = id_in; 47288ef16d8STomasz Nowicki 47388ef16d8STomasz Nowicki return NULL; 47488ef16d8STomasz Nowicki } 47588ef16d8STomasz Nowicki 476e3d49392SLorenzo Pieralisi static struct acpi_iort_node *iort_node_map_platform_id( 477e3d49392SLorenzo Pieralisi struct acpi_iort_node *node, u32 *id_out, u8 type_mask, 4788ca4f1d3SHanjun Guo int index) 4798ca4f1d3SHanjun Guo { 4808ca4f1d3SHanjun Guo struct acpi_iort_node *parent; 4818ca4f1d3SHanjun Guo u32 id; 4828ca4f1d3SHanjun Guo 4838ca4f1d3SHanjun Guo /* step 1: retrieve the initial dev id */ 4848ca4f1d3SHanjun Guo parent = iort_node_get_id(node, &id, index); 4858ca4f1d3SHanjun Guo if (!parent) 4868ca4f1d3SHanjun Guo return NULL; 4878ca4f1d3SHanjun Guo 4888ca4f1d3SHanjun Guo /* 4898ca4f1d3SHanjun Guo * optional step 2: map the initial dev id if its parent is not 4908ca4f1d3SHanjun Guo * the target type we want, map it again for the use cases such 4918ca4f1d3SHanjun Guo * as NC (named component) -> SMMU -> ITS. If the type is matched, 4928ca4f1d3SHanjun Guo * return the initial dev id and its parent pointer directly. 4938ca4f1d3SHanjun Guo */ 4948ca4f1d3SHanjun Guo if (!(IORT_TYPE_MASK(parent->type) & type_mask)) 4958ca4f1d3SHanjun Guo parent = iort_node_map_id(parent, id, id_out, type_mask); 4968ca4f1d3SHanjun Guo else 4978ca4f1d3SHanjun Guo if (id_out) 4988ca4f1d3SHanjun Guo *id_out = id; 4998ca4f1d3SHanjun Guo 5008ca4f1d3SHanjun Guo return parent; 5018ca4f1d3SHanjun Guo } 5028ca4f1d3SHanjun Guo 50388ef16d8STomasz Nowicki static struct acpi_iort_node *iort_find_dev_node(struct device *dev) 50488ef16d8STomasz Nowicki { 50588ef16d8STomasz Nowicki struct pci_bus *pbus; 50688ef16d8STomasz Nowicki 5070a71d8b9SHanjun Guo if (!dev_is_pci(dev)) { 5080a71d8b9SHanjun Guo struct acpi_iort_node *node; 5090a71d8b9SHanjun Guo /* 5100a71d8b9SHanjun Guo * scan iort_fwnode_list to see if it's an iort platform 5110a71d8b9SHanjun Guo * device (such as SMMU, PMCG),its iort node already cached 5120a71d8b9SHanjun Guo * and associated with fwnode when iort platform devices 5130a71d8b9SHanjun Guo * were initialized. 5140a71d8b9SHanjun Guo */ 5150a71d8b9SHanjun Guo node = iort_get_iort_node(dev->fwnode); 5160a71d8b9SHanjun Guo if (node) 5170a71d8b9SHanjun Guo return node; 5180a71d8b9SHanjun Guo 5190a71d8b9SHanjun Guo /* 5200a71d8b9SHanjun Guo * if not, then it should be a platform device defined in 5210a71d8b9SHanjun Guo * DSDT/SSDT (with Named Component node in IORT) 5220a71d8b9SHanjun Guo */ 52388ef16d8STomasz Nowicki return iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT, 52488ef16d8STomasz Nowicki iort_match_node_callback, dev); 5250a71d8b9SHanjun Guo } 52688ef16d8STomasz Nowicki 52788ef16d8STomasz Nowicki /* Find a PCI root bus */ 52888ef16d8STomasz Nowicki pbus = to_pci_dev(dev)->bus; 52988ef16d8STomasz Nowicki while (!pci_is_root_bus(pbus)) 53088ef16d8STomasz Nowicki pbus = pbus->parent; 53188ef16d8STomasz Nowicki 53288ef16d8STomasz Nowicki return iort_scan_node(ACPI_IORT_NODE_PCI_ROOT_COMPLEX, 53388ef16d8STomasz Nowicki iort_match_node_callback, &pbus->dev); 53488ef16d8STomasz Nowicki } 53588ef16d8STomasz Nowicki 5364bf2efd2STomasz Nowicki /** 5374bf2efd2STomasz Nowicki * iort_msi_map_rid() - Map a MSI requester ID for a device 5384bf2efd2STomasz Nowicki * @dev: The device for which the mapping is to be done. 5394bf2efd2STomasz Nowicki * @req_id: The device requester ID. 5404bf2efd2STomasz Nowicki * 5414bf2efd2STomasz Nowicki * Returns: mapped MSI RID on success, input requester ID otherwise 5424bf2efd2STomasz Nowicki */ 5434bf2efd2STomasz Nowicki u32 iort_msi_map_rid(struct device *dev, u32 req_id) 5444bf2efd2STomasz Nowicki { 5454bf2efd2STomasz Nowicki struct acpi_iort_node *node; 5464bf2efd2STomasz Nowicki u32 dev_id; 5474bf2efd2STomasz Nowicki 5484bf2efd2STomasz Nowicki node = iort_find_dev_node(dev); 5494bf2efd2STomasz Nowicki if (!node) 5504bf2efd2STomasz Nowicki return req_id; 5514bf2efd2STomasz Nowicki 552697f6093SHanjun Guo iort_node_map_id(node, req_id, &dev_id, IORT_MSI_TYPE); 5534bf2efd2STomasz Nowicki return dev_id; 5544bf2efd2STomasz Nowicki } 5554bf2efd2STomasz Nowicki 5564bf2efd2STomasz Nowicki /** 557ae7c1838SHanjun Guo * iort_pmsi_get_dev_id() - Get the device id for a device 558ae7c1838SHanjun Guo * @dev: The device for which the mapping is to be done. 559ae7c1838SHanjun Guo * @dev_id: The device ID found. 560ae7c1838SHanjun Guo * 561ae7c1838SHanjun Guo * Returns: 0 for successful find a dev id, -ENODEV on error 562ae7c1838SHanjun Guo */ 563ae7c1838SHanjun Guo int iort_pmsi_get_dev_id(struct device *dev, u32 *dev_id) 564ae7c1838SHanjun Guo { 5658c8df8dcSHanjun Guo int i, index; 566ae7c1838SHanjun Guo struct acpi_iort_node *node; 567ae7c1838SHanjun Guo 568ae7c1838SHanjun Guo node = iort_find_dev_node(dev); 569ae7c1838SHanjun Guo if (!node) 570ae7c1838SHanjun Guo return -ENODEV; 571ae7c1838SHanjun Guo 5728c8df8dcSHanjun Guo index = iort_get_id_mapping_index(node); 5738c8df8dcSHanjun Guo /* if there is a valid index, go get the dev_id directly */ 5748c8df8dcSHanjun Guo if (index >= 0) { 5758c8df8dcSHanjun Guo if (iort_node_get_id(node, dev_id, index)) 576ae7c1838SHanjun Guo return 0; 5778c8df8dcSHanjun Guo } else { 5788c8df8dcSHanjun Guo for (i = 0; i < node->mapping_count; i++) { 5798c8df8dcSHanjun Guo if (iort_node_map_platform_id(node, dev_id, 5808c8df8dcSHanjun Guo IORT_MSI_TYPE, i)) 5818c8df8dcSHanjun Guo return 0; 5828c8df8dcSHanjun Guo } 583ae7c1838SHanjun Guo } 584ae7c1838SHanjun Guo 585ae7c1838SHanjun Guo return -ENODEV; 586ae7c1838SHanjun Guo } 587ae7c1838SHanjun Guo 588*8b4282e6SShameer Kolothum static int __maybe_unused iort_find_its_base(u32 its_id, phys_addr_t *base) 589*8b4282e6SShameer Kolothum { 590*8b4282e6SShameer Kolothum struct iort_its_msi_chip *its_msi_chip; 591*8b4282e6SShameer Kolothum int ret = -ENODEV; 592*8b4282e6SShameer Kolothum 593*8b4282e6SShameer Kolothum spin_lock(&iort_msi_chip_lock); 594*8b4282e6SShameer Kolothum list_for_each_entry(its_msi_chip, &iort_msi_chip_list, list) { 595*8b4282e6SShameer Kolothum if (its_msi_chip->translation_id == its_id) { 596*8b4282e6SShameer Kolothum *base = its_msi_chip->base_addr; 597*8b4282e6SShameer Kolothum ret = 0; 598*8b4282e6SShameer Kolothum break; 599*8b4282e6SShameer Kolothum } 600*8b4282e6SShameer Kolothum } 601*8b4282e6SShameer Kolothum spin_unlock(&iort_msi_chip_lock); 602*8b4282e6SShameer Kolothum 603*8b4282e6SShameer Kolothum return ret; 604*8b4282e6SShameer Kolothum } 605*8b4282e6SShameer Kolothum 606ae7c1838SHanjun Guo /** 6074bf2efd2STomasz Nowicki * iort_dev_find_its_id() - Find the ITS identifier for a device 6084bf2efd2STomasz Nowicki * @dev: The device. 6096cb6bf56SHanjun Guo * @req_id: Device's requester ID 6104bf2efd2STomasz Nowicki * @idx: Index of the ITS identifier list. 6114bf2efd2STomasz Nowicki * @its_id: ITS identifier. 6124bf2efd2STomasz Nowicki * 6134bf2efd2STomasz Nowicki * Returns: 0 on success, appropriate error value otherwise 6144bf2efd2STomasz Nowicki */ 6154bf2efd2STomasz Nowicki static int iort_dev_find_its_id(struct device *dev, u32 req_id, 6164bf2efd2STomasz Nowicki unsigned int idx, int *its_id) 6174bf2efd2STomasz Nowicki { 6184bf2efd2STomasz Nowicki struct acpi_iort_its_group *its; 6194bf2efd2STomasz Nowicki struct acpi_iort_node *node; 6204bf2efd2STomasz Nowicki 6214bf2efd2STomasz Nowicki node = iort_find_dev_node(dev); 6224bf2efd2STomasz Nowicki if (!node) 6234bf2efd2STomasz Nowicki return -ENXIO; 6244bf2efd2STomasz Nowicki 625697f6093SHanjun Guo node = iort_node_map_id(node, req_id, NULL, IORT_MSI_TYPE); 6264bf2efd2STomasz Nowicki if (!node) 6274bf2efd2STomasz Nowicki return -ENXIO; 6284bf2efd2STomasz Nowicki 6294bf2efd2STomasz Nowicki /* Move to ITS specific data */ 6304bf2efd2STomasz Nowicki its = (struct acpi_iort_its_group *)node->node_data; 6314bf2efd2STomasz Nowicki if (idx > its->its_count) { 6324bf2efd2STomasz Nowicki dev_err(dev, "requested ITS ID index [%d] is greater than available [%d]\n", 6334bf2efd2STomasz Nowicki idx, its->its_count); 6344bf2efd2STomasz Nowicki return -ENXIO; 6354bf2efd2STomasz Nowicki } 6364bf2efd2STomasz Nowicki 6374bf2efd2STomasz Nowicki *its_id = its->identifiers[idx]; 6384bf2efd2STomasz Nowicki return 0; 6394bf2efd2STomasz Nowicki } 6404bf2efd2STomasz Nowicki 6414bf2efd2STomasz Nowicki /** 6424bf2efd2STomasz Nowicki * iort_get_device_domain() - Find MSI domain related to a device 6434bf2efd2STomasz Nowicki * @dev: The device. 6444bf2efd2STomasz Nowicki * @req_id: Requester ID for the device. 6454bf2efd2STomasz Nowicki * 6464bf2efd2STomasz Nowicki * Returns: the MSI domain for this device, NULL otherwise 6474bf2efd2STomasz Nowicki */ 6484bf2efd2STomasz Nowicki struct irq_domain *iort_get_device_domain(struct device *dev, u32 req_id) 6494bf2efd2STomasz Nowicki { 6504bf2efd2STomasz Nowicki struct fwnode_handle *handle; 6514bf2efd2STomasz Nowicki int its_id; 6524bf2efd2STomasz Nowicki 6534bf2efd2STomasz Nowicki if (iort_dev_find_its_id(dev, req_id, 0, &its_id)) 6544bf2efd2STomasz Nowicki return NULL; 6554bf2efd2STomasz Nowicki 6564bf2efd2STomasz Nowicki handle = iort_find_domain_token(its_id); 6574bf2efd2STomasz Nowicki if (!handle) 6584bf2efd2STomasz Nowicki return NULL; 6594bf2efd2STomasz Nowicki 6604bf2efd2STomasz Nowicki return irq_find_matching_fwnode(handle, DOMAIN_BUS_PCI_MSI); 6614bf2efd2STomasz Nowicki } 6624bf2efd2STomasz Nowicki 66365637901SLorenzo Pieralisi static void iort_set_device_domain(struct device *dev, 66465637901SLorenzo Pieralisi struct acpi_iort_node *node) 66565637901SLorenzo Pieralisi { 66665637901SLorenzo Pieralisi struct acpi_iort_its_group *its; 66765637901SLorenzo Pieralisi struct acpi_iort_node *msi_parent; 66865637901SLorenzo Pieralisi struct acpi_iort_id_mapping *map; 66965637901SLorenzo Pieralisi struct fwnode_handle *iort_fwnode; 67065637901SLorenzo Pieralisi struct irq_domain *domain; 67165637901SLorenzo Pieralisi int index; 67265637901SLorenzo Pieralisi 67365637901SLorenzo Pieralisi index = iort_get_id_mapping_index(node); 67465637901SLorenzo Pieralisi if (index < 0) 67565637901SLorenzo Pieralisi return; 67665637901SLorenzo Pieralisi 67765637901SLorenzo Pieralisi map = ACPI_ADD_PTR(struct acpi_iort_id_mapping, node, 67865637901SLorenzo Pieralisi node->mapping_offset + index * sizeof(*map)); 67965637901SLorenzo Pieralisi 68065637901SLorenzo Pieralisi /* Firmware bug! */ 68165637901SLorenzo Pieralisi if (!map->output_reference || 68265637901SLorenzo Pieralisi !(map->flags & ACPI_IORT_ID_SINGLE_MAPPING)) { 68365637901SLorenzo Pieralisi pr_err(FW_BUG "[node %p type %d] Invalid MSI mapping\n", 68465637901SLorenzo Pieralisi node, node->type); 68565637901SLorenzo Pieralisi return; 68665637901SLorenzo Pieralisi } 68765637901SLorenzo Pieralisi 68865637901SLorenzo Pieralisi msi_parent = ACPI_ADD_PTR(struct acpi_iort_node, iort_table, 68965637901SLorenzo Pieralisi map->output_reference); 69065637901SLorenzo Pieralisi 69165637901SLorenzo Pieralisi if (!msi_parent || msi_parent->type != ACPI_IORT_NODE_ITS_GROUP) 69265637901SLorenzo Pieralisi return; 69365637901SLorenzo Pieralisi 69465637901SLorenzo Pieralisi /* Move to ITS specific data */ 69565637901SLorenzo Pieralisi its = (struct acpi_iort_its_group *)msi_parent->node_data; 69665637901SLorenzo Pieralisi 69765637901SLorenzo Pieralisi iort_fwnode = iort_find_domain_token(its->identifiers[0]); 69865637901SLorenzo Pieralisi if (!iort_fwnode) 69965637901SLorenzo Pieralisi return; 70065637901SLorenzo Pieralisi 70165637901SLorenzo Pieralisi domain = irq_find_matching_fwnode(iort_fwnode, DOMAIN_BUS_PLATFORM_MSI); 70265637901SLorenzo Pieralisi if (domain) 70365637901SLorenzo Pieralisi dev_set_msi_domain(dev, domain); 70465637901SLorenzo Pieralisi } 70565637901SLorenzo Pieralisi 706d4f54a18SHanjun Guo /** 707d4f54a18SHanjun Guo * iort_get_platform_device_domain() - Find MSI domain related to a 708d4f54a18SHanjun Guo * platform device 709d4f54a18SHanjun Guo * @dev: the dev pointer associated with the platform device 710d4f54a18SHanjun Guo * 711d4f54a18SHanjun Guo * Returns: the MSI domain for this device, NULL otherwise 712d4f54a18SHanjun Guo */ 713d4f54a18SHanjun Guo static struct irq_domain *iort_get_platform_device_domain(struct device *dev) 714d4f54a18SHanjun Guo { 715d4f54a18SHanjun Guo struct acpi_iort_node *node, *msi_parent; 716d4f54a18SHanjun Guo struct fwnode_handle *iort_fwnode; 717d4f54a18SHanjun Guo struct acpi_iort_its_group *its; 718d4f54a18SHanjun Guo int i; 719d4f54a18SHanjun Guo 720d4f54a18SHanjun Guo /* find its associated iort node */ 721d4f54a18SHanjun Guo node = iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT, 722d4f54a18SHanjun Guo iort_match_node_callback, dev); 723d4f54a18SHanjun Guo if (!node) 724d4f54a18SHanjun Guo return NULL; 725d4f54a18SHanjun Guo 726d4f54a18SHanjun Guo /* then find its msi parent node */ 727d4f54a18SHanjun Guo for (i = 0; i < node->mapping_count; i++) { 728d4f54a18SHanjun Guo msi_parent = iort_node_map_platform_id(node, NULL, 729d4f54a18SHanjun Guo IORT_MSI_TYPE, i); 730d4f54a18SHanjun Guo if (msi_parent) 731d4f54a18SHanjun Guo break; 732d4f54a18SHanjun Guo } 733d4f54a18SHanjun Guo 734d4f54a18SHanjun Guo if (!msi_parent) 735d4f54a18SHanjun Guo return NULL; 736d4f54a18SHanjun Guo 737d4f54a18SHanjun Guo /* Move to ITS specific data */ 738d4f54a18SHanjun Guo its = (struct acpi_iort_its_group *)msi_parent->node_data; 739d4f54a18SHanjun Guo 740d4f54a18SHanjun Guo iort_fwnode = iort_find_domain_token(its->identifiers[0]); 741d4f54a18SHanjun Guo if (!iort_fwnode) 742d4f54a18SHanjun Guo return NULL; 743d4f54a18SHanjun Guo 744d4f54a18SHanjun Guo return irq_find_matching_fwnode(iort_fwnode, DOMAIN_BUS_PLATFORM_MSI); 745d4f54a18SHanjun Guo } 746d4f54a18SHanjun Guo 747d4f54a18SHanjun Guo void acpi_configure_pmsi_domain(struct device *dev) 748d4f54a18SHanjun Guo { 749d4f54a18SHanjun Guo struct irq_domain *msi_domain; 750d4f54a18SHanjun Guo 751d4f54a18SHanjun Guo msi_domain = iort_get_platform_device_domain(dev); 752d4f54a18SHanjun Guo if (msi_domain) 753d4f54a18SHanjun Guo dev_set_msi_domain(dev, msi_domain); 754d4f54a18SHanjun Guo } 755d4f54a18SHanjun Guo 756bc8648d4SRobin Murphy static int __maybe_unused __get_pci_rid(struct pci_dev *pdev, u16 alias, 757bc8648d4SRobin Murphy void *data) 758643b8e4dSLorenzo Pieralisi { 759643b8e4dSLorenzo Pieralisi u32 *rid = data; 760643b8e4dSLorenzo Pieralisi 761643b8e4dSLorenzo Pieralisi *rid = alias; 762643b8e4dSLorenzo Pieralisi return 0; 763643b8e4dSLorenzo Pieralisi } 764643b8e4dSLorenzo Pieralisi 765643b8e4dSLorenzo Pieralisi static int arm_smmu_iort_xlate(struct device *dev, u32 streamid, 766643b8e4dSLorenzo Pieralisi struct fwnode_handle *fwnode, 767643b8e4dSLorenzo Pieralisi const struct iommu_ops *ops) 768643b8e4dSLorenzo Pieralisi { 769643b8e4dSLorenzo Pieralisi int ret = iommu_fwspec_init(dev, fwnode, ops); 770643b8e4dSLorenzo Pieralisi 771643b8e4dSLorenzo Pieralisi if (!ret) 772643b8e4dSLorenzo Pieralisi ret = iommu_fwspec_add_ids(dev, &streamid, 1); 773643b8e4dSLorenzo Pieralisi 774643b8e4dSLorenzo Pieralisi return ret; 775643b8e4dSLorenzo Pieralisi } 776643b8e4dSLorenzo Pieralisi 7771d9029d4SLorenzo Pieralisi static inline bool iort_iommu_driver_enabled(u8 type) 7781d9029d4SLorenzo Pieralisi { 7791d9029d4SLorenzo Pieralisi switch (type) { 7801d9029d4SLorenzo Pieralisi case ACPI_IORT_NODE_SMMU_V3: 7811d9029d4SLorenzo Pieralisi return IS_BUILTIN(CONFIG_ARM_SMMU_V3); 7821d9029d4SLorenzo Pieralisi case ACPI_IORT_NODE_SMMU: 7831d9029d4SLorenzo Pieralisi return IS_BUILTIN(CONFIG_ARM_SMMU); 7841d9029d4SLorenzo Pieralisi default: 7851d9029d4SLorenzo Pieralisi pr_warn("IORT node type %u does not describe an SMMU\n", type); 7861d9029d4SLorenzo Pieralisi return false; 7871d9029d4SLorenzo Pieralisi } 7881d9029d4SLorenzo Pieralisi } 7891d9029d4SLorenzo Pieralisi 790d49f2dedSLorenzo Pieralisi #ifdef CONFIG_IOMMU_API 791*8b4282e6SShameer Kolothum static struct acpi_iort_node *iort_get_msi_resv_iommu(struct device *dev) 792*8b4282e6SShameer Kolothum { 793*8b4282e6SShameer Kolothum struct acpi_iort_node *iommu; 794*8b4282e6SShameer Kolothum struct iommu_fwspec *fwspec = dev->iommu_fwspec; 795*8b4282e6SShameer Kolothum 796*8b4282e6SShameer Kolothum iommu = iort_get_iort_node(fwspec->iommu_fwnode); 797*8b4282e6SShameer Kolothum 798*8b4282e6SShameer Kolothum if (iommu && (iommu->type == ACPI_IORT_NODE_SMMU_V3)) { 799*8b4282e6SShameer Kolothum struct acpi_iort_smmu_v3 *smmu; 800*8b4282e6SShameer Kolothum 801*8b4282e6SShameer Kolothum smmu = (struct acpi_iort_smmu_v3 *)iommu->node_data; 802*8b4282e6SShameer Kolothum if (smmu->model == ACPI_IORT_SMMU_V3_HISILICON_HI161X) 803*8b4282e6SShameer Kolothum return iommu; 804*8b4282e6SShameer Kolothum } 805*8b4282e6SShameer Kolothum 806*8b4282e6SShameer Kolothum return NULL; 807*8b4282e6SShameer Kolothum } 808*8b4282e6SShameer Kolothum 809e3d49392SLorenzo Pieralisi static inline const struct iommu_ops *iort_fwspec_iommu_ops( 810e3d49392SLorenzo Pieralisi struct iommu_fwspec *fwspec) 811d49f2dedSLorenzo Pieralisi { 812d49f2dedSLorenzo Pieralisi return (fwspec && fwspec->ops) ? fwspec->ops : NULL; 813d49f2dedSLorenzo Pieralisi } 814d49f2dedSLorenzo Pieralisi 815e3d49392SLorenzo Pieralisi static inline int iort_add_device_replay(const struct iommu_ops *ops, 816e3d49392SLorenzo Pieralisi struct device *dev) 817d49f2dedSLorenzo Pieralisi { 818d49f2dedSLorenzo Pieralisi int err = 0; 819d49f2dedSLorenzo Pieralisi 820bc8648d4SRobin Murphy if (ops->add_device && dev->bus && !dev->iommu_group) 821d49f2dedSLorenzo Pieralisi err = ops->add_device(dev); 822d49f2dedSLorenzo Pieralisi 823d49f2dedSLorenzo Pieralisi return err; 824d49f2dedSLorenzo Pieralisi } 825*8b4282e6SShameer Kolothum 826*8b4282e6SShameer Kolothum /** 827*8b4282e6SShameer Kolothum * iort_iommu_msi_get_resv_regions - Reserved region driver helper 828*8b4282e6SShameer Kolothum * @dev: Device from iommu_get_resv_regions() 829*8b4282e6SShameer Kolothum * @head: Reserved region list from iommu_get_resv_regions() 830*8b4282e6SShameer Kolothum * 831*8b4282e6SShameer Kolothum * Returns: Number of msi reserved regions on success (0 if platform 832*8b4282e6SShameer Kolothum * doesn't require the reservation or no associated msi regions), 833*8b4282e6SShameer Kolothum * appropriate error value otherwise. The ITS interrupt translation 834*8b4282e6SShameer Kolothum * spaces (ITS_base + SZ_64K, SZ_64K) associated with the device 835*8b4282e6SShameer Kolothum * are the msi reserved regions. 836*8b4282e6SShameer Kolothum */ 837*8b4282e6SShameer Kolothum int iort_iommu_msi_get_resv_regions(struct device *dev, struct list_head *head) 838*8b4282e6SShameer Kolothum { 839*8b4282e6SShameer Kolothum struct acpi_iort_its_group *its; 840*8b4282e6SShameer Kolothum struct acpi_iort_node *iommu_node, *its_node = NULL; 841*8b4282e6SShameer Kolothum int i, resv = 0; 842*8b4282e6SShameer Kolothum 843*8b4282e6SShameer Kolothum iommu_node = iort_get_msi_resv_iommu(dev); 844*8b4282e6SShameer Kolothum if (!iommu_node) 845*8b4282e6SShameer Kolothum return 0; 846*8b4282e6SShameer Kolothum 847*8b4282e6SShameer Kolothum /* 848*8b4282e6SShameer Kolothum * Current logic to reserve ITS regions relies on HW topologies 849*8b4282e6SShameer Kolothum * where a given PCI or named component maps its IDs to only one 850*8b4282e6SShameer Kolothum * ITS group; if a PCI or named component can map its IDs to 851*8b4282e6SShameer Kolothum * different ITS groups through IORT mappings this function has 852*8b4282e6SShameer Kolothum * to be reworked to ensure we reserve regions for all ITS groups 853*8b4282e6SShameer Kolothum * a given PCI or named component may map IDs to. 854*8b4282e6SShameer Kolothum */ 855*8b4282e6SShameer Kolothum 856*8b4282e6SShameer Kolothum for (i = 0; i < dev->iommu_fwspec->num_ids; i++) { 857*8b4282e6SShameer Kolothum its_node = iort_node_map_id(iommu_node, 858*8b4282e6SShameer Kolothum dev->iommu_fwspec->ids[i], 859*8b4282e6SShameer Kolothum NULL, IORT_MSI_TYPE); 860*8b4282e6SShameer Kolothum if (its_node) 861*8b4282e6SShameer Kolothum break; 862*8b4282e6SShameer Kolothum } 863*8b4282e6SShameer Kolothum 864*8b4282e6SShameer Kolothum if (!its_node) 865*8b4282e6SShameer Kolothum return 0; 866*8b4282e6SShameer Kolothum 867*8b4282e6SShameer Kolothum /* Move to ITS specific data */ 868*8b4282e6SShameer Kolothum its = (struct acpi_iort_its_group *)its_node->node_data; 869*8b4282e6SShameer Kolothum 870*8b4282e6SShameer Kolothum for (i = 0; i < its->its_count; i++) { 871*8b4282e6SShameer Kolothum phys_addr_t base; 872*8b4282e6SShameer Kolothum 873*8b4282e6SShameer Kolothum if (!iort_find_its_base(its->identifiers[i], &base)) { 874*8b4282e6SShameer Kolothum int prot = IOMMU_WRITE | IOMMU_NOEXEC | IOMMU_MMIO; 875*8b4282e6SShameer Kolothum struct iommu_resv_region *region; 876*8b4282e6SShameer Kolothum 877*8b4282e6SShameer Kolothum region = iommu_alloc_resv_region(base + SZ_64K, SZ_64K, 878*8b4282e6SShameer Kolothum prot, IOMMU_RESV_MSI); 879*8b4282e6SShameer Kolothum if (region) { 880*8b4282e6SShameer Kolothum list_add_tail(®ion->list, head); 881*8b4282e6SShameer Kolothum resv++; 882*8b4282e6SShameer Kolothum } 883*8b4282e6SShameer Kolothum } 884*8b4282e6SShameer Kolothum } 885*8b4282e6SShameer Kolothum 886*8b4282e6SShameer Kolothum return (resv == its->its_count) ? resv : -ENODEV; 887*8b4282e6SShameer Kolothum } 888d49f2dedSLorenzo Pieralisi #else 889e3d49392SLorenzo Pieralisi static inline const struct iommu_ops *iort_fwspec_iommu_ops( 890e3d49392SLorenzo Pieralisi struct iommu_fwspec *fwspec) 891d49f2dedSLorenzo Pieralisi { return NULL; } 892e3d49392SLorenzo Pieralisi static inline int iort_add_device_replay(const struct iommu_ops *ops, 893e3d49392SLorenzo Pieralisi struct device *dev) 894d49f2dedSLorenzo Pieralisi { return 0; } 895*8b4282e6SShameer Kolothum int iort_iommu_msi_get_resv_regions(struct device *dev, struct list_head *head) 896*8b4282e6SShameer Kolothum { return 0; } 897d49f2dedSLorenzo Pieralisi #endif 898d49f2dedSLorenzo Pieralisi 899bc8648d4SRobin Murphy static int iort_iommu_xlate(struct device *dev, struct acpi_iort_node *node, 900643b8e4dSLorenzo Pieralisi u32 streamid) 901643b8e4dSLorenzo Pieralisi { 902bc8648d4SRobin Murphy const struct iommu_ops *ops; 903643b8e4dSLorenzo Pieralisi struct fwnode_handle *iort_fwnode; 904643b8e4dSLorenzo Pieralisi 905bc8648d4SRobin Murphy if (!node) 906bc8648d4SRobin Murphy return -ENODEV; 907bc8648d4SRobin Murphy 908643b8e4dSLorenzo Pieralisi iort_fwnode = iort_get_fwnode(node); 909643b8e4dSLorenzo Pieralisi if (!iort_fwnode) 910bc8648d4SRobin Murphy return -ENODEV; 911643b8e4dSLorenzo Pieralisi 9125a1bb638SSricharan R /* 9135a1bb638SSricharan R * If the ops look-up fails, this means that either 9145a1bb638SSricharan R * the SMMU drivers have not been probed yet or that 9155a1bb638SSricharan R * the SMMU drivers are not built in the kernel; 9165a1bb638SSricharan R * Depending on whether the SMMU drivers are built-in 9175a1bb638SSricharan R * in the kernel or not, defer the IOMMU configuration 9185a1bb638SSricharan R * or just abort it. 9195a1bb638SSricharan R */ 920bc8648d4SRobin Murphy ops = iommu_ops_from_fwnode(iort_fwnode); 921643b8e4dSLorenzo Pieralisi if (!ops) 9225a1bb638SSricharan R return iort_iommu_driver_enabled(node->type) ? 923bc8648d4SRobin Murphy -EPROBE_DEFER : -ENODEV; 924643b8e4dSLorenzo Pieralisi 925bc8648d4SRobin Murphy return arm_smmu_iort_xlate(dev, streamid, iort_fwnode, ops); 926643b8e4dSLorenzo Pieralisi } 927643b8e4dSLorenzo Pieralisi 928bc8648d4SRobin Murphy struct iort_pci_alias_info { 929bc8648d4SRobin Murphy struct device *dev; 930bc8648d4SRobin Murphy struct acpi_iort_node *node; 931bc8648d4SRobin Murphy }; 932bc8648d4SRobin Murphy 933bc8648d4SRobin Murphy static int iort_pci_iommu_init(struct pci_dev *pdev, u16 alias, void *data) 934bc8648d4SRobin Murphy { 935bc8648d4SRobin Murphy struct iort_pci_alias_info *info = data; 936bc8648d4SRobin Murphy struct acpi_iort_node *parent; 937bc8648d4SRobin Murphy u32 streamid; 938bc8648d4SRobin Murphy 939bc8648d4SRobin Murphy parent = iort_node_map_id(info->node, alias, &streamid, 940bc8648d4SRobin Murphy IORT_IOMMU_TYPE); 941bc8648d4SRobin Murphy return iort_iommu_xlate(info->dev, parent, streamid); 942643b8e4dSLorenzo Pieralisi } 943643b8e4dSLorenzo Pieralisi 94410d8ab2cSLorenzo Pieralisi static int nc_dma_get_range(struct device *dev, u64 *size) 94510d8ab2cSLorenzo Pieralisi { 94610d8ab2cSLorenzo Pieralisi struct acpi_iort_node *node; 94710d8ab2cSLorenzo Pieralisi struct acpi_iort_named_component *ncomp; 94810d8ab2cSLorenzo Pieralisi 94910d8ab2cSLorenzo Pieralisi node = iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT, 95010d8ab2cSLorenzo Pieralisi iort_match_node_callback, dev); 95110d8ab2cSLorenzo Pieralisi if (!node) 95210d8ab2cSLorenzo Pieralisi return -ENODEV; 95310d8ab2cSLorenzo Pieralisi 95410d8ab2cSLorenzo Pieralisi ncomp = (struct acpi_iort_named_component *)node->node_data; 95510d8ab2cSLorenzo Pieralisi 95610d8ab2cSLorenzo Pieralisi *size = ncomp->memory_address_limit >= 64 ? U64_MAX : 95710d8ab2cSLorenzo Pieralisi 1ULL<<ncomp->memory_address_limit; 95810d8ab2cSLorenzo Pieralisi 95910d8ab2cSLorenzo Pieralisi return 0; 96010d8ab2cSLorenzo Pieralisi } 96110d8ab2cSLorenzo Pieralisi 962643b8e4dSLorenzo Pieralisi /** 9637ad42639SLorenzo Pieralisi * iort_dma_setup() - Set-up device DMA parameters. 96418b709beSLorenzo Pieralisi * 96518b709beSLorenzo Pieralisi * @dev: device to configure 9667ad42639SLorenzo Pieralisi * @dma_addr: device DMA address result pointer 9677ad42639SLorenzo Pieralisi * @size: DMA range size result pointer 96818b709beSLorenzo Pieralisi */ 9697ad42639SLorenzo Pieralisi void iort_dma_setup(struct device *dev, u64 *dma_addr, u64 *dma_size) 97018b709beSLorenzo Pieralisi { 9717ad42639SLorenzo Pieralisi u64 mask, dmaaddr = 0, size = 0, offset = 0; 9727ad42639SLorenzo Pieralisi int ret, msb; 9737ad42639SLorenzo Pieralisi 97418b709beSLorenzo Pieralisi /* 97518b709beSLorenzo Pieralisi * Set default coherent_dma_mask to 32 bit. Drivers are expected to 97618b709beSLorenzo Pieralisi * setup the correct supported mask. 97718b709beSLorenzo Pieralisi */ 97818b709beSLorenzo Pieralisi if (!dev->coherent_dma_mask) 97918b709beSLorenzo Pieralisi dev->coherent_dma_mask = DMA_BIT_MASK(32); 98018b709beSLorenzo Pieralisi 98118b709beSLorenzo Pieralisi /* 98218b709beSLorenzo Pieralisi * Set it to coherent_dma_mask by default if the architecture 98318b709beSLorenzo Pieralisi * code has not set it. 98418b709beSLorenzo Pieralisi */ 98518b709beSLorenzo Pieralisi if (!dev->dma_mask) 98618b709beSLorenzo Pieralisi dev->dma_mask = &dev->coherent_dma_mask; 9877ad42639SLorenzo Pieralisi 9887ad42639SLorenzo Pieralisi size = max(dev->coherent_dma_mask, dev->coherent_dma_mask + 1); 9897ad42639SLorenzo Pieralisi 99010d8ab2cSLorenzo Pieralisi if (dev_is_pci(dev)) 9917ad42639SLorenzo Pieralisi ret = acpi_dma_get_range(dev, &dmaaddr, &offset, &size); 99210d8ab2cSLorenzo Pieralisi else 99310d8ab2cSLorenzo Pieralisi ret = nc_dma_get_range(dev, &size); 99410d8ab2cSLorenzo Pieralisi 9957ad42639SLorenzo Pieralisi if (!ret) { 9967ad42639SLorenzo Pieralisi msb = fls64(dmaaddr + size - 1); 9977ad42639SLorenzo Pieralisi /* 9987ad42639SLorenzo Pieralisi * Round-up to the power-of-two mask or set 9997ad42639SLorenzo Pieralisi * the mask to the whole 64-bit address space 10007ad42639SLorenzo Pieralisi * in case the DMA region covers the full 10017ad42639SLorenzo Pieralisi * memory window. 10027ad42639SLorenzo Pieralisi */ 10037ad42639SLorenzo Pieralisi mask = msb == 64 ? U64_MAX : (1ULL << msb) - 1; 10047ad42639SLorenzo Pieralisi /* 10057ad42639SLorenzo Pieralisi * Limit coherent and dma mask based on size 10067ad42639SLorenzo Pieralisi * retrieved from firmware. 10077ad42639SLorenzo Pieralisi */ 10087ad42639SLorenzo Pieralisi dev->coherent_dma_mask = mask; 10097ad42639SLorenzo Pieralisi *dev->dma_mask = mask; 10107ad42639SLorenzo Pieralisi } 10117ad42639SLorenzo Pieralisi 10127ad42639SLorenzo Pieralisi *dma_addr = dmaaddr; 10137ad42639SLorenzo Pieralisi *dma_size = size; 10147ad42639SLorenzo Pieralisi 10157ad42639SLorenzo Pieralisi dev->dma_pfn_offset = PFN_DOWN(offset); 10167ad42639SLorenzo Pieralisi dev_dbg(dev, "dma_pfn_offset(%#08llx)\n", offset); 101718b709beSLorenzo Pieralisi } 101818b709beSLorenzo Pieralisi 101918b709beSLorenzo Pieralisi /** 1020643b8e4dSLorenzo Pieralisi * iort_iommu_configure - Set-up IOMMU configuration for a device. 1021643b8e4dSLorenzo Pieralisi * 1022643b8e4dSLorenzo Pieralisi * @dev: device to configure 1023643b8e4dSLorenzo Pieralisi * 1024643b8e4dSLorenzo Pieralisi * Returns: iommu_ops pointer on configuration success 1025643b8e4dSLorenzo Pieralisi * NULL on configuration failure 1026643b8e4dSLorenzo Pieralisi */ 1027643b8e4dSLorenzo Pieralisi const struct iommu_ops *iort_iommu_configure(struct device *dev) 1028643b8e4dSLorenzo Pieralisi { 1029643b8e4dSLorenzo Pieralisi struct acpi_iort_node *node, *parent; 1030bc8648d4SRobin Murphy const struct iommu_ops *ops; 1031643b8e4dSLorenzo Pieralisi u32 streamid = 0; 1032bc8648d4SRobin Murphy int err = -ENODEV; 1033643b8e4dSLorenzo Pieralisi 10344dac3210SLorenzo Pieralisi /* 10354dac3210SLorenzo Pieralisi * If we already translated the fwspec there 10364dac3210SLorenzo Pieralisi * is nothing left to do, return the iommu_ops. 10374dac3210SLorenzo Pieralisi */ 10384dac3210SLorenzo Pieralisi ops = iort_fwspec_iommu_ops(dev->iommu_fwspec); 10394dac3210SLorenzo Pieralisi if (ops) 10404dac3210SLorenzo Pieralisi return ops; 10414dac3210SLorenzo Pieralisi 1042643b8e4dSLorenzo Pieralisi if (dev_is_pci(dev)) { 1043643b8e4dSLorenzo Pieralisi struct pci_bus *bus = to_pci_dev(dev)->bus; 1044bc8648d4SRobin Murphy struct iort_pci_alias_info info = { .dev = dev }; 1045643b8e4dSLorenzo Pieralisi 1046643b8e4dSLorenzo Pieralisi node = iort_scan_node(ACPI_IORT_NODE_PCI_ROOT_COMPLEX, 1047643b8e4dSLorenzo Pieralisi iort_match_node_callback, &bus->dev); 1048643b8e4dSLorenzo Pieralisi if (!node) 1049643b8e4dSLorenzo Pieralisi return NULL; 1050643b8e4dSLorenzo Pieralisi 1051bc8648d4SRobin Murphy info.node = node; 1052bc8648d4SRobin Murphy err = pci_for_each_dma_alias(to_pci_dev(dev), 1053bc8648d4SRobin Murphy iort_pci_iommu_init, &info); 1054643b8e4dSLorenzo Pieralisi } else { 1055643b8e4dSLorenzo Pieralisi int i = 0; 1056643b8e4dSLorenzo Pieralisi 1057643b8e4dSLorenzo Pieralisi node = iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT, 1058643b8e4dSLorenzo Pieralisi iort_match_node_callback, dev); 1059643b8e4dSLorenzo Pieralisi if (!node) 1060643b8e4dSLorenzo Pieralisi return NULL; 1061643b8e4dSLorenzo Pieralisi 1062bc8648d4SRobin Murphy do { 10638ca4f1d3SHanjun Guo parent = iort_node_map_platform_id(node, &streamid, 10648ca4f1d3SHanjun Guo IORT_IOMMU_TYPE, 10658ca4f1d3SHanjun Guo i++); 1066bc8648d4SRobin Murphy 1067bc8648d4SRobin Murphy if (parent) 1068bc8648d4SRobin Murphy err = iort_iommu_xlate(dev, parent, streamid); 1069bc8648d4SRobin Murphy } while (parent && !err); 1070643b8e4dSLorenzo Pieralisi } 1071643b8e4dSLorenzo Pieralisi 10725a1bb638SSricharan R /* 10735a1bb638SSricharan R * If we have reason to believe the IOMMU driver missed the initial 10745a1bb638SSricharan R * add_device callback for dev, replay it to get things in order. 10755a1bb638SSricharan R */ 1076bc8648d4SRobin Murphy if (!err) { 10774d36037aSArnd Bergmann ops = iort_fwspec_iommu_ops(dev->iommu_fwspec); 1078d49f2dedSLorenzo Pieralisi err = iort_add_device_replay(ops, dev); 1079bc8648d4SRobin Murphy } 10805a1bb638SSricharan R 1081058f8c3fSSricharan R /* Ignore all other errors apart from EPROBE_DEFER */ 1082bc8648d4SRobin Murphy if (err == -EPROBE_DEFER) { 1083bc8648d4SRobin Murphy ops = ERR_PTR(err); 1084bc8648d4SRobin Murphy } else if (err) { 1085bc8648d4SRobin Murphy dev_dbg(dev, "Adding to IOMMU failed: %d\n", err); 1086058f8c3fSSricharan R ops = NULL; 1087058f8c3fSSricharan R } 1088058f8c3fSSricharan R 1089643b8e4dSLorenzo Pieralisi return ops; 1090643b8e4dSLorenzo Pieralisi } 1091643b8e4dSLorenzo Pieralisi 1092e4dadfa8SLorenzo Pieralisi static void __init acpi_iort_register_irq(int hwirq, const char *name, 1093e4dadfa8SLorenzo Pieralisi int trigger, 1094e4dadfa8SLorenzo Pieralisi struct resource *res) 1095e4dadfa8SLorenzo Pieralisi { 1096e4dadfa8SLorenzo Pieralisi int irq = acpi_register_gsi(NULL, hwirq, trigger, 1097e4dadfa8SLorenzo Pieralisi ACPI_ACTIVE_HIGH); 1098e4dadfa8SLorenzo Pieralisi 1099e4dadfa8SLorenzo Pieralisi if (irq <= 0) { 1100e4dadfa8SLorenzo Pieralisi pr_err("could not register gsi hwirq %d name [%s]\n", hwirq, 1101e4dadfa8SLorenzo Pieralisi name); 1102e4dadfa8SLorenzo Pieralisi return; 1103e4dadfa8SLorenzo Pieralisi } 1104e4dadfa8SLorenzo Pieralisi 1105e4dadfa8SLorenzo Pieralisi res->start = irq; 1106e4dadfa8SLorenzo Pieralisi res->end = irq; 1107e4dadfa8SLorenzo Pieralisi res->flags = IORESOURCE_IRQ; 1108e4dadfa8SLorenzo Pieralisi res->name = name; 1109e4dadfa8SLorenzo Pieralisi } 1110e4dadfa8SLorenzo Pieralisi 1111e4dadfa8SLorenzo Pieralisi static int __init arm_smmu_v3_count_resources(struct acpi_iort_node *node) 1112e4dadfa8SLorenzo Pieralisi { 1113e4dadfa8SLorenzo Pieralisi struct acpi_iort_smmu_v3 *smmu; 1114e4dadfa8SLorenzo Pieralisi /* Always present mem resource */ 1115e4dadfa8SLorenzo Pieralisi int num_res = 1; 1116e4dadfa8SLorenzo Pieralisi 1117e4dadfa8SLorenzo Pieralisi /* Retrieve SMMUv3 specific data */ 1118e4dadfa8SLorenzo Pieralisi smmu = (struct acpi_iort_smmu_v3 *)node->node_data; 1119e4dadfa8SLorenzo Pieralisi 1120e4dadfa8SLorenzo Pieralisi if (smmu->event_gsiv) 1121e4dadfa8SLorenzo Pieralisi num_res++; 1122e4dadfa8SLorenzo Pieralisi 1123e4dadfa8SLorenzo Pieralisi if (smmu->pri_gsiv) 1124e4dadfa8SLorenzo Pieralisi num_res++; 1125e4dadfa8SLorenzo Pieralisi 1126e4dadfa8SLorenzo Pieralisi if (smmu->gerr_gsiv) 1127e4dadfa8SLorenzo Pieralisi num_res++; 1128e4dadfa8SLorenzo Pieralisi 1129e4dadfa8SLorenzo Pieralisi if (smmu->sync_gsiv) 1130e4dadfa8SLorenzo Pieralisi num_res++; 1131e4dadfa8SLorenzo Pieralisi 1132e4dadfa8SLorenzo Pieralisi return num_res; 1133e4dadfa8SLorenzo Pieralisi } 1134e4dadfa8SLorenzo Pieralisi 1135f935448aSGeetha Sowjanya static bool arm_smmu_v3_is_combined_irq(struct acpi_iort_smmu_v3 *smmu) 1136f935448aSGeetha Sowjanya { 1137f935448aSGeetha Sowjanya /* 1138f935448aSGeetha Sowjanya * Cavium ThunderX2 implementation doesn't not support unique 1139f935448aSGeetha Sowjanya * irq line. Use single irq line for all the SMMUv3 interrupts. 1140f935448aSGeetha Sowjanya */ 1141f935448aSGeetha Sowjanya if (smmu->model != ACPI_IORT_SMMU_V3_CAVIUM_CN99XX) 1142f935448aSGeetha Sowjanya return false; 1143f935448aSGeetha Sowjanya 1144f935448aSGeetha Sowjanya /* 1145f935448aSGeetha Sowjanya * ThunderX2 doesn't support MSIs from the SMMU, so we're checking 1146f935448aSGeetha Sowjanya * SPI numbers here. 1147f935448aSGeetha Sowjanya */ 1148f935448aSGeetha Sowjanya return smmu->event_gsiv == smmu->pri_gsiv && 1149f935448aSGeetha Sowjanya smmu->event_gsiv == smmu->gerr_gsiv && 1150f935448aSGeetha Sowjanya smmu->event_gsiv == smmu->sync_gsiv; 1151f935448aSGeetha Sowjanya } 1152f935448aSGeetha Sowjanya 1153403e8c7cSLinu Cherian static unsigned long arm_smmu_v3_resource_size(struct acpi_iort_smmu_v3 *smmu) 1154403e8c7cSLinu Cherian { 1155403e8c7cSLinu Cherian /* 1156403e8c7cSLinu Cherian * Override the size, for Cavium ThunderX2 implementation 1157403e8c7cSLinu Cherian * which doesn't support the page 1 SMMU register space. 1158403e8c7cSLinu Cherian */ 1159403e8c7cSLinu Cherian if (smmu->model == ACPI_IORT_SMMU_V3_CAVIUM_CN99XX) 1160403e8c7cSLinu Cherian return SZ_64K; 1161403e8c7cSLinu Cherian 1162403e8c7cSLinu Cherian return SZ_128K; 1163403e8c7cSLinu Cherian } 1164403e8c7cSLinu Cherian 1165e4dadfa8SLorenzo Pieralisi static void __init arm_smmu_v3_init_resources(struct resource *res, 1166e4dadfa8SLorenzo Pieralisi struct acpi_iort_node *node) 1167e4dadfa8SLorenzo Pieralisi { 1168e4dadfa8SLorenzo Pieralisi struct acpi_iort_smmu_v3 *smmu; 1169e4dadfa8SLorenzo Pieralisi int num_res = 0; 1170e4dadfa8SLorenzo Pieralisi 1171e4dadfa8SLorenzo Pieralisi /* Retrieve SMMUv3 specific data */ 1172e4dadfa8SLorenzo Pieralisi smmu = (struct acpi_iort_smmu_v3 *)node->node_data; 1173e4dadfa8SLorenzo Pieralisi 1174e4dadfa8SLorenzo Pieralisi res[num_res].start = smmu->base_address; 1175403e8c7cSLinu Cherian res[num_res].end = smmu->base_address + 1176403e8c7cSLinu Cherian arm_smmu_v3_resource_size(smmu) - 1; 1177e4dadfa8SLorenzo Pieralisi res[num_res].flags = IORESOURCE_MEM; 1178e4dadfa8SLorenzo Pieralisi 1179e4dadfa8SLorenzo Pieralisi num_res++; 1180f935448aSGeetha Sowjanya if (arm_smmu_v3_is_combined_irq(smmu)) { 1181f935448aSGeetha Sowjanya if (smmu->event_gsiv) 1182f935448aSGeetha Sowjanya acpi_iort_register_irq(smmu->event_gsiv, "combined", 1183f935448aSGeetha Sowjanya ACPI_EDGE_SENSITIVE, 1184f935448aSGeetha Sowjanya &res[num_res++]); 1185f935448aSGeetha Sowjanya } else { 1186e4dadfa8SLorenzo Pieralisi 1187e4dadfa8SLorenzo Pieralisi if (smmu->event_gsiv) 1188e4dadfa8SLorenzo Pieralisi acpi_iort_register_irq(smmu->event_gsiv, "eventq", 1189e4dadfa8SLorenzo Pieralisi ACPI_EDGE_SENSITIVE, 1190e4dadfa8SLorenzo Pieralisi &res[num_res++]); 1191e4dadfa8SLorenzo Pieralisi 1192e4dadfa8SLorenzo Pieralisi if (smmu->pri_gsiv) 1193e4dadfa8SLorenzo Pieralisi acpi_iort_register_irq(smmu->pri_gsiv, "priq", 1194e4dadfa8SLorenzo Pieralisi ACPI_EDGE_SENSITIVE, 1195e4dadfa8SLorenzo Pieralisi &res[num_res++]); 1196e4dadfa8SLorenzo Pieralisi 1197e4dadfa8SLorenzo Pieralisi if (smmu->gerr_gsiv) 1198e4dadfa8SLorenzo Pieralisi acpi_iort_register_irq(smmu->gerr_gsiv, "gerror", 1199e4dadfa8SLorenzo Pieralisi ACPI_EDGE_SENSITIVE, 1200e4dadfa8SLorenzo Pieralisi &res[num_res++]); 1201e4dadfa8SLorenzo Pieralisi 1202e4dadfa8SLorenzo Pieralisi if (smmu->sync_gsiv) 1203e4dadfa8SLorenzo Pieralisi acpi_iort_register_irq(smmu->sync_gsiv, "cmdq-sync", 1204e4dadfa8SLorenzo Pieralisi ACPI_EDGE_SENSITIVE, 1205e4dadfa8SLorenzo Pieralisi &res[num_res++]); 1206e4dadfa8SLorenzo Pieralisi } 1207f935448aSGeetha Sowjanya } 1208e4dadfa8SLorenzo Pieralisi 1209e4dadfa8SLorenzo Pieralisi static bool __init arm_smmu_v3_is_coherent(struct acpi_iort_node *node) 1210e4dadfa8SLorenzo Pieralisi { 1211e4dadfa8SLorenzo Pieralisi struct acpi_iort_smmu_v3 *smmu; 1212e4dadfa8SLorenzo Pieralisi 1213e4dadfa8SLorenzo Pieralisi /* Retrieve SMMUv3 specific data */ 1214e4dadfa8SLorenzo Pieralisi smmu = (struct acpi_iort_smmu_v3 *)node->node_data; 1215e4dadfa8SLorenzo Pieralisi 1216e4dadfa8SLorenzo Pieralisi return smmu->flags & ACPI_IORT_SMMU_V3_COHACC_OVERRIDE; 1217e4dadfa8SLorenzo Pieralisi } 1218e4dadfa8SLorenzo Pieralisi 121975808131SLorenzo Pieralisi #if defined(CONFIG_ACPI_NUMA) 12205fe0ce3bSGanapatrao Kulkarni /* 12215fe0ce3bSGanapatrao Kulkarni * set numa proximity domain for smmuv3 device 12225fe0ce3bSGanapatrao Kulkarni */ 12235fe0ce3bSGanapatrao Kulkarni static void __init arm_smmu_v3_set_proximity(struct device *dev, 12245fe0ce3bSGanapatrao Kulkarni struct acpi_iort_node *node) 12255fe0ce3bSGanapatrao Kulkarni { 12265fe0ce3bSGanapatrao Kulkarni struct acpi_iort_smmu_v3 *smmu; 12275fe0ce3bSGanapatrao Kulkarni 12285fe0ce3bSGanapatrao Kulkarni smmu = (struct acpi_iort_smmu_v3 *)node->node_data; 12295fe0ce3bSGanapatrao Kulkarni if (smmu->flags & ACPI_IORT_SMMU_V3_PXM_VALID) { 12305fe0ce3bSGanapatrao Kulkarni set_dev_node(dev, acpi_map_pxm_to_node(smmu->pxm)); 12315fe0ce3bSGanapatrao Kulkarni pr_info("SMMU-v3[%llx] Mapped to Proximity domain %d\n", 12325fe0ce3bSGanapatrao Kulkarni smmu->base_address, 12335fe0ce3bSGanapatrao Kulkarni smmu->pxm); 12345fe0ce3bSGanapatrao Kulkarni } 12355fe0ce3bSGanapatrao Kulkarni } 12365fe0ce3bSGanapatrao Kulkarni #else 12375fe0ce3bSGanapatrao Kulkarni #define arm_smmu_v3_set_proximity NULL 12385fe0ce3bSGanapatrao Kulkarni #endif 12395fe0ce3bSGanapatrao Kulkarni 1240d6fcd3b1SLorenzo Pieralisi static int __init arm_smmu_count_resources(struct acpi_iort_node *node) 1241d6fcd3b1SLorenzo Pieralisi { 1242d6fcd3b1SLorenzo Pieralisi struct acpi_iort_smmu *smmu; 1243d6fcd3b1SLorenzo Pieralisi 1244d6fcd3b1SLorenzo Pieralisi /* Retrieve SMMU specific data */ 1245d6fcd3b1SLorenzo Pieralisi smmu = (struct acpi_iort_smmu *)node->node_data; 1246d6fcd3b1SLorenzo Pieralisi 1247d6fcd3b1SLorenzo Pieralisi /* 1248d6fcd3b1SLorenzo Pieralisi * Only consider the global fault interrupt and ignore the 1249d6fcd3b1SLorenzo Pieralisi * configuration access interrupt. 1250d6fcd3b1SLorenzo Pieralisi * 1251d6fcd3b1SLorenzo Pieralisi * MMIO address and global fault interrupt resources are always 1252d6fcd3b1SLorenzo Pieralisi * present so add them to the context interrupt count as a static 1253d6fcd3b1SLorenzo Pieralisi * value. 1254d6fcd3b1SLorenzo Pieralisi */ 1255d6fcd3b1SLorenzo Pieralisi return smmu->context_interrupt_count + 2; 1256d6fcd3b1SLorenzo Pieralisi } 1257d6fcd3b1SLorenzo Pieralisi 1258d6fcd3b1SLorenzo Pieralisi static void __init arm_smmu_init_resources(struct resource *res, 1259d6fcd3b1SLorenzo Pieralisi struct acpi_iort_node *node) 1260d6fcd3b1SLorenzo Pieralisi { 1261d6fcd3b1SLorenzo Pieralisi struct acpi_iort_smmu *smmu; 1262d6fcd3b1SLorenzo Pieralisi int i, hw_irq, trigger, num_res = 0; 1263d6fcd3b1SLorenzo Pieralisi u64 *ctx_irq, *glb_irq; 1264d6fcd3b1SLorenzo Pieralisi 1265d6fcd3b1SLorenzo Pieralisi /* Retrieve SMMU specific data */ 1266d6fcd3b1SLorenzo Pieralisi smmu = (struct acpi_iort_smmu *)node->node_data; 1267d6fcd3b1SLorenzo Pieralisi 1268d6fcd3b1SLorenzo Pieralisi res[num_res].start = smmu->base_address; 1269d6fcd3b1SLorenzo Pieralisi res[num_res].end = smmu->base_address + smmu->span - 1; 1270d6fcd3b1SLorenzo Pieralisi res[num_res].flags = IORESOURCE_MEM; 1271d6fcd3b1SLorenzo Pieralisi num_res++; 1272d6fcd3b1SLorenzo Pieralisi 1273d6fcd3b1SLorenzo Pieralisi glb_irq = ACPI_ADD_PTR(u64, node, smmu->global_interrupt_offset); 1274d6fcd3b1SLorenzo Pieralisi /* Global IRQs */ 1275d6fcd3b1SLorenzo Pieralisi hw_irq = IORT_IRQ_MASK(glb_irq[0]); 1276d6fcd3b1SLorenzo Pieralisi trigger = IORT_IRQ_TRIGGER_MASK(glb_irq[0]); 1277d6fcd3b1SLorenzo Pieralisi 1278d6fcd3b1SLorenzo Pieralisi acpi_iort_register_irq(hw_irq, "arm-smmu-global", trigger, 1279d6fcd3b1SLorenzo Pieralisi &res[num_res++]); 1280d6fcd3b1SLorenzo Pieralisi 1281d6fcd3b1SLorenzo Pieralisi /* Context IRQs */ 1282d6fcd3b1SLorenzo Pieralisi ctx_irq = ACPI_ADD_PTR(u64, node, smmu->context_interrupt_offset); 1283d6fcd3b1SLorenzo Pieralisi for (i = 0; i < smmu->context_interrupt_count; i++) { 1284d6fcd3b1SLorenzo Pieralisi hw_irq = IORT_IRQ_MASK(ctx_irq[i]); 1285d6fcd3b1SLorenzo Pieralisi trigger = IORT_IRQ_TRIGGER_MASK(ctx_irq[i]); 1286d6fcd3b1SLorenzo Pieralisi 1287d6fcd3b1SLorenzo Pieralisi acpi_iort_register_irq(hw_irq, "arm-smmu-context", trigger, 1288d6fcd3b1SLorenzo Pieralisi &res[num_res++]); 1289d6fcd3b1SLorenzo Pieralisi } 1290d6fcd3b1SLorenzo Pieralisi } 1291d6fcd3b1SLorenzo Pieralisi 1292d6fcd3b1SLorenzo Pieralisi static bool __init arm_smmu_is_coherent(struct acpi_iort_node *node) 1293d6fcd3b1SLorenzo Pieralisi { 1294d6fcd3b1SLorenzo Pieralisi struct acpi_iort_smmu *smmu; 1295d6fcd3b1SLorenzo Pieralisi 1296d6fcd3b1SLorenzo Pieralisi /* Retrieve SMMU specific data */ 1297d6fcd3b1SLorenzo Pieralisi smmu = (struct acpi_iort_smmu *)node->node_data; 1298d6fcd3b1SLorenzo Pieralisi 1299d6fcd3b1SLorenzo Pieralisi return smmu->flags & ACPI_IORT_SMMU_COHERENT_WALK; 1300d6fcd3b1SLorenzo Pieralisi } 1301d6fcd3b1SLorenzo Pieralisi 1302896dd2c3SLorenzo Pieralisi struct iort_dev_config { 1303846f0e9eSLorenzo Pieralisi const char *name; 1304896dd2c3SLorenzo Pieralisi int (*dev_init)(struct acpi_iort_node *node); 1305896dd2c3SLorenzo Pieralisi bool (*dev_is_coherent)(struct acpi_iort_node *node); 1306896dd2c3SLorenzo Pieralisi int (*dev_count_resources)(struct acpi_iort_node *node); 1307896dd2c3SLorenzo Pieralisi void (*dev_init_resources)(struct resource *res, 1308846f0e9eSLorenzo Pieralisi struct acpi_iort_node *node); 1309896dd2c3SLorenzo Pieralisi void (*dev_set_proximity)(struct device *dev, 13105fe0ce3bSGanapatrao Kulkarni struct acpi_iort_node *node); 1311846f0e9eSLorenzo Pieralisi }; 1312846f0e9eSLorenzo Pieralisi 1313896dd2c3SLorenzo Pieralisi static const struct iort_dev_config iort_arm_smmu_v3_cfg __initconst = { 1314e4dadfa8SLorenzo Pieralisi .name = "arm-smmu-v3", 1315896dd2c3SLorenzo Pieralisi .dev_is_coherent = arm_smmu_v3_is_coherent, 1316896dd2c3SLorenzo Pieralisi .dev_count_resources = arm_smmu_v3_count_resources, 1317896dd2c3SLorenzo Pieralisi .dev_init_resources = arm_smmu_v3_init_resources, 1318896dd2c3SLorenzo Pieralisi .dev_set_proximity = arm_smmu_v3_set_proximity, 1319e4dadfa8SLorenzo Pieralisi }; 1320e4dadfa8SLorenzo Pieralisi 1321896dd2c3SLorenzo Pieralisi static const struct iort_dev_config iort_arm_smmu_cfg __initconst = { 1322d6fcd3b1SLorenzo Pieralisi .name = "arm-smmu", 1323896dd2c3SLorenzo Pieralisi .dev_is_coherent = arm_smmu_is_coherent, 1324896dd2c3SLorenzo Pieralisi .dev_count_resources = arm_smmu_count_resources, 1325896dd2c3SLorenzo Pieralisi .dev_init_resources = arm_smmu_init_resources 1326d6fcd3b1SLorenzo Pieralisi }; 1327d6fcd3b1SLorenzo Pieralisi 1328896dd2c3SLorenzo Pieralisi static __init const struct iort_dev_config *iort_get_dev_cfg( 1329e3d49392SLorenzo Pieralisi struct acpi_iort_node *node) 1330846f0e9eSLorenzo Pieralisi { 1331e4dadfa8SLorenzo Pieralisi switch (node->type) { 1332e4dadfa8SLorenzo Pieralisi case ACPI_IORT_NODE_SMMU_V3: 1333e4dadfa8SLorenzo Pieralisi return &iort_arm_smmu_v3_cfg; 1334d6fcd3b1SLorenzo Pieralisi case ACPI_IORT_NODE_SMMU: 1335d6fcd3b1SLorenzo Pieralisi return &iort_arm_smmu_cfg; 1336e4dadfa8SLorenzo Pieralisi default: 1337846f0e9eSLorenzo Pieralisi return NULL; 1338846f0e9eSLorenzo Pieralisi } 1339e4dadfa8SLorenzo Pieralisi } 1340846f0e9eSLorenzo Pieralisi 1341846f0e9eSLorenzo Pieralisi /** 1342896dd2c3SLorenzo Pieralisi * iort_add_platform_device() - Allocate a platform device for IORT node 1343896dd2c3SLorenzo Pieralisi * @node: Pointer to device ACPI IORT node 1344846f0e9eSLorenzo Pieralisi * 1345846f0e9eSLorenzo Pieralisi * Returns: 0 on success, <0 failure 1346846f0e9eSLorenzo Pieralisi */ 1347896dd2c3SLorenzo Pieralisi static int __init iort_add_platform_device(struct acpi_iort_node *node, 1348896dd2c3SLorenzo Pieralisi const struct iort_dev_config *ops) 1349846f0e9eSLorenzo Pieralisi { 1350846f0e9eSLorenzo Pieralisi struct fwnode_handle *fwnode; 1351846f0e9eSLorenzo Pieralisi struct platform_device *pdev; 1352846f0e9eSLorenzo Pieralisi struct resource *r; 1353846f0e9eSLorenzo Pieralisi enum dev_dma_attr attr; 1354846f0e9eSLorenzo Pieralisi int ret, count; 1355846f0e9eSLorenzo Pieralisi 1356846f0e9eSLorenzo Pieralisi pdev = platform_device_alloc(ops->name, PLATFORM_DEVID_AUTO); 1357846f0e9eSLorenzo Pieralisi if (!pdev) 13585e5afa6cSDan Carpenter return -ENOMEM; 1359846f0e9eSLorenzo Pieralisi 1360896dd2c3SLorenzo Pieralisi if (ops->dev_set_proximity) 1361896dd2c3SLorenzo Pieralisi ops->dev_set_proximity(&pdev->dev, node); 13625fe0ce3bSGanapatrao Kulkarni 1363896dd2c3SLorenzo Pieralisi count = ops->dev_count_resources(node); 1364846f0e9eSLorenzo Pieralisi 1365846f0e9eSLorenzo Pieralisi r = kcalloc(count, sizeof(*r), GFP_KERNEL); 1366846f0e9eSLorenzo Pieralisi if (!r) { 1367846f0e9eSLorenzo Pieralisi ret = -ENOMEM; 1368846f0e9eSLorenzo Pieralisi goto dev_put; 1369846f0e9eSLorenzo Pieralisi } 1370846f0e9eSLorenzo Pieralisi 1371896dd2c3SLorenzo Pieralisi ops->dev_init_resources(r, node); 1372846f0e9eSLorenzo Pieralisi 1373846f0e9eSLorenzo Pieralisi ret = platform_device_add_resources(pdev, r, count); 1374846f0e9eSLorenzo Pieralisi /* 1375846f0e9eSLorenzo Pieralisi * Resources are duplicated in platform_device_add_resources, 1376846f0e9eSLorenzo Pieralisi * free their allocated memory 1377846f0e9eSLorenzo Pieralisi */ 1378846f0e9eSLorenzo Pieralisi kfree(r); 1379846f0e9eSLorenzo Pieralisi 1380846f0e9eSLorenzo Pieralisi if (ret) 1381846f0e9eSLorenzo Pieralisi goto dev_put; 1382846f0e9eSLorenzo Pieralisi 1383846f0e9eSLorenzo Pieralisi /* 1384846f0e9eSLorenzo Pieralisi * Add a copy of IORT node pointer to platform_data to 1385846f0e9eSLorenzo Pieralisi * be used to retrieve IORT data information. 1386846f0e9eSLorenzo Pieralisi */ 1387846f0e9eSLorenzo Pieralisi ret = platform_device_add_data(pdev, &node, sizeof(node)); 1388846f0e9eSLorenzo Pieralisi if (ret) 1389846f0e9eSLorenzo Pieralisi goto dev_put; 1390846f0e9eSLorenzo Pieralisi 1391846f0e9eSLorenzo Pieralisi /* 1392846f0e9eSLorenzo Pieralisi * We expect the dma masks to be equivalent for 1393846f0e9eSLorenzo Pieralisi * all SMMUs set-ups 1394846f0e9eSLorenzo Pieralisi */ 1395846f0e9eSLorenzo Pieralisi pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask; 1396846f0e9eSLorenzo Pieralisi 1397846f0e9eSLorenzo Pieralisi fwnode = iort_get_fwnode(node); 1398846f0e9eSLorenzo Pieralisi 1399846f0e9eSLorenzo Pieralisi if (!fwnode) { 1400846f0e9eSLorenzo Pieralisi ret = -ENODEV; 1401846f0e9eSLorenzo Pieralisi goto dev_put; 1402846f0e9eSLorenzo Pieralisi } 1403846f0e9eSLorenzo Pieralisi 1404846f0e9eSLorenzo Pieralisi pdev->dev.fwnode = fwnode; 1405846f0e9eSLorenzo Pieralisi 1406896dd2c3SLorenzo Pieralisi attr = ops->dev_is_coherent && ops->dev_is_coherent(node) ? 1407846f0e9eSLorenzo Pieralisi DEV_DMA_COHERENT : DEV_DMA_NON_COHERENT; 1408846f0e9eSLorenzo Pieralisi 1409846f0e9eSLorenzo Pieralisi /* Configure DMA for the page table walker */ 1410846f0e9eSLorenzo Pieralisi acpi_dma_configure(&pdev->dev, attr); 1411846f0e9eSLorenzo Pieralisi 141265637901SLorenzo Pieralisi iort_set_device_domain(&pdev->dev, node); 141365637901SLorenzo Pieralisi 1414846f0e9eSLorenzo Pieralisi ret = platform_device_add(pdev); 1415846f0e9eSLorenzo Pieralisi if (ret) 1416846f0e9eSLorenzo Pieralisi goto dma_deconfigure; 1417846f0e9eSLorenzo Pieralisi 1418846f0e9eSLorenzo Pieralisi return 0; 1419846f0e9eSLorenzo Pieralisi 1420846f0e9eSLorenzo Pieralisi dma_deconfigure: 1421846f0e9eSLorenzo Pieralisi acpi_dma_deconfigure(&pdev->dev); 1422846f0e9eSLorenzo Pieralisi dev_put: 1423846f0e9eSLorenzo Pieralisi platform_device_put(pdev); 1424846f0e9eSLorenzo Pieralisi 1425846f0e9eSLorenzo Pieralisi return ret; 1426846f0e9eSLorenzo Pieralisi } 1427846f0e9eSLorenzo Pieralisi 142837f6b42eSLorenzo Pieralisi static bool __init iort_enable_acs(struct acpi_iort_node *iort_node) 142937f6b42eSLorenzo Pieralisi { 143037f6b42eSLorenzo Pieralisi if (iort_node->type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX) { 143137f6b42eSLorenzo Pieralisi struct acpi_iort_node *parent; 143237f6b42eSLorenzo Pieralisi struct acpi_iort_id_mapping *map; 143337f6b42eSLorenzo Pieralisi int i; 143437f6b42eSLorenzo Pieralisi 143537f6b42eSLorenzo Pieralisi map = ACPI_ADD_PTR(struct acpi_iort_id_mapping, iort_node, 143637f6b42eSLorenzo Pieralisi iort_node->mapping_offset); 143737f6b42eSLorenzo Pieralisi 143837f6b42eSLorenzo Pieralisi for (i = 0; i < iort_node->mapping_count; i++, map++) { 143937f6b42eSLorenzo Pieralisi if (!map->output_reference) 144037f6b42eSLorenzo Pieralisi continue; 144137f6b42eSLorenzo Pieralisi 144237f6b42eSLorenzo Pieralisi parent = ACPI_ADD_PTR(struct acpi_iort_node, 144337f6b42eSLorenzo Pieralisi iort_table, map->output_reference); 144437f6b42eSLorenzo Pieralisi /* 144537f6b42eSLorenzo Pieralisi * If we detect a RC->SMMU mapping, make sure 144637f6b42eSLorenzo Pieralisi * we enable ACS on the system. 144737f6b42eSLorenzo Pieralisi */ 144837f6b42eSLorenzo Pieralisi if ((parent->type == ACPI_IORT_NODE_SMMU) || 144937f6b42eSLorenzo Pieralisi (parent->type == ACPI_IORT_NODE_SMMU_V3)) { 145037f6b42eSLorenzo Pieralisi pci_request_acs(); 145137f6b42eSLorenzo Pieralisi return true; 145237f6b42eSLorenzo Pieralisi } 145337f6b42eSLorenzo Pieralisi } 145437f6b42eSLorenzo Pieralisi } 145537f6b42eSLorenzo Pieralisi 145637f6b42eSLorenzo Pieralisi return false; 145737f6b42eSLorenzo Pieralisi } 145837f6b42eSLorenzo Pieralisi 1459846f0e9eSLorenzo Pieralisi static void __init iort_init_platform_devices(void) 1460846f0e9eSLorenzo Pieralisi { 1461846f0e9eSLorenzo Pieralisi struct acpi_iort_node *iort_node, *iort_end; 1462846f0e9eSLorenzo Pieralisi struct acpi_table_iort *iort; 1463846f0e9eSLorenzo Pieralisi struct fwnode_handle *fwnode; 1464846f0e9eSLorenzo Pieralisi int i, ret; 146537f6b42eSLorenzo Pieralisi bool acs_enabled = false; 1466896dd2c3SLorenzo Pieralisi const struct iort_dev_config *ops; 1467846f0e9eSLorenzo Pieralisi 1468846f0e9eSLorenzo Pieralisi /* 1469846f0e9eSLorenzo Pieralisi * iort_table and iort both point to the start of IORT table, but 1470846f0e9eSLorenzo Pieralisi * have different struct types 1471846f0e9eSLorenzo Pieralisi */ 1472846f0e9eSLorenzo Pieralisi iort = (struct acpi_table_iort *)iort_table; 1473846f0e9eSLorenzo Pieralisi 1474846f0e9eSLorenzo Pieralisi /* Get the first IORT node */ 1475846f0e9eSLorenzo Pieralisi iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort, 1476846f0e9eSLorenzo Pieralisi iort->node_offset); 1477846f0e9eSLorenzo Pieralisi iort_end = ACPI_ADD_PTR(struct acpi_iort_node, iort, 1478846f0e9eSLorenzo Pieralisi iort_table->length); 1479846f0e9eSLorenzo Pieralisi 1480846f0e9eSLorenzo Pieralisi for (i = 0; i < iort->node_count; i++) { 1481846f0e9eSLorenzo Pieralisi if (iort_node >= iort_end) { 1482846f0e9eSLorenzo Pieralisi pr_err("iort node pointer overflows, bad table\n"); 1483846f0e9eSLorenzo Pieralisi return; 1484846f0e9eSLorenzo Pieralisi } 1485846f0e9eSLorenzo Pieralisi 148637f6b42eSLorenzo Pieralisi if (!acs_enabled) 148737f6b42eSLorenzo Pieralisi acs_enabled = iort_enable_acs(iort_node); 148837f6b42eSLorenzo Pieralisi 1489896dd2c3SLorenzo Pieralisi ops = iort_get_dev_cfg(iort_node); 1490896dd2c3SLorenzo Pieralisi if (ops) { 1491846f0e9eSLorenzo Pieralisi fwnode = acpi_alloc_fwnode_static(); 1492846f0e9eSLorenzo Pieralisi if (!fwnode) 1493846f0e9eSLorenzo Pieralisi return; 1494846f0e9eSLorenzo Pieralisi 1495846f0e9eSLorenzo Pieralisi iort_set_fwnode(iort_node, fwnode); 1496846f0e9eSLorenzo Pieralisi 1497896dd2c3SLorenzo Pieralisi ret = iort_add_platform_device(iort_node, ops); 1498846f0e9eSLorenzo Pieralisi if (ret) { 1499846f0e9eSLorenzo Pieralisi iort_delete_fwnode(iort_node); 1500846f0e9eSLorenzo Pieralisi acpi_free_fwnode_static(fwnode); 1501846f0e9eSLorenzo Pieralisi return; 1502846f0e9eSLorenzo Pieralisi } 1503846f0e9eSLorenzo Pieralisi } 1504846f0e9eSLorenzo Pieralisi 1505846f0e9eSLorenzo Pieralisi iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort_node, 1506846f0e9eSLorenzo Pieralisi iort_node->length); 1507846f0e9eSLorenzo Pieralisi } 1508846f0e9eSLorenzo Pieralisi } 1509846f0e9eSLorenzo Pieralisi 151088ef16d8STomasz Nowicki void __init acpi_iort_init(void) 151188ef16d8STomasz Nowicki { 151288ef16d8STomasz Nowicki acpi_status status; 151388ef16d8STomasz Nowicki 151488ef16d8STomasz Nowicki status = acpi_get_table(ACPI_SIG_IORT, 0, &iort_table); 151534ceea27SLorenzo Pieralisi if (ACPI_FAILURE(status)) { 151634ceea27SLorenzo Pieralisi if (status != AE_NOT_FOUND) { 151788ef16d8STomasz Nowicki const char *msg = acpi_format_exception(status); 151834ceea27SLorenzo Pieralisi 151988ef16d8STomasz Nowicki pr_err("Failed to get table, %s\n", msg); 152088ef16d8STomasz Nowicki } 152134ceea27SLorenzo Pieralisi 152234ceea27SLorenzo Pieralisi return; 152334ceea27SLorenzo Pieralisi } 152434ceea27SLorenzo Pieralisi 1525846f0e9eSLorenzo Pieralisi iort_init_platform_devices(); 152688ef16d8STomasz Nowicki } 1527