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 344bf2efd2STomasz Nowicki struct iort_its_msi_chip { 354bf2efd2STomasz Nowicki struct list_head list; 364bf2efd2STomasz Nowicki struct fwnode_handle *fw_node; 378b4282e6SShameer Kolothum phys_addr_t base_addr; 384bf2efd2STomasz Nowicki u32 translation_id; 394bf2efd2STomasz Nowicki }; 404bf2efd2STomasz Nowicki 417936df92SLorenzo Pieralisi struct iort_fwnode { 427936df92SLorenzo Pieralisi struct list_head list; 437936df92SLorenzo Pieralisi struct acpi_iort_node *iort_node; 447936df92SLorenzo Pieralisi struct fwnode_handle *fwnode; 457936df92SLorenzo Pieralisi }; 467936df92SLorenzo Pieralisi static LIST_HEAD(iort_fwnode_list); 477936df92SLorenzo Pieralisi static DEFINE_SPINLOCK(iort_fwnode_lock); 487936df92SLorenzo Pieralisi 497936df92SLorenzo Pieralisi /** 507936df92SLorenzo Pieralisi * iort_set_fwnode() - Create iort_fwnode and use it to register 517936df92SLorenzo Pieralisi * iommu data in the iort_fwnode_list 527936df92SLorenzo Pieralisi * 537936df92SLorenzo Pieralisi * @node: IORT table node associated with the IOMMU 547936df92SLorenzo Pieralisi * @fwnode: fwnode associated with the IORT node 557936df92SLorenzo Pieralisi * 567936df92SLorenzo Pieralisi * Returns: 0 on success 577936df92SLorenzo Pieralisi * <0 on failure 587936df92SLorenzo Pieralisi */ 597936df92SLorenzo Pieralisi static inline int iort_set_fwnode(struct acpi_iort_node *iort_node, 607936df92SLorenzo Pieralisi struct fwnode_handle *fwnode) 617936df92SLorenzo Pieralisi { 627936df92SLorenzo Pieralisi struct iort_fwnode *np; 637936df92SLorenzo Pieralisi 647936df92SLorenzo Pieralisi np = kzalloc(sizeof(struct iort_fwnode), GFP_ATOMIC); 657936df92SLorenzo Pieralisi 667936df92SLorenzo Pieralisi if (WARN_ON(!np)) 677936df92SLorenzo Pieralisi return -ENOMEM; 687936df92SLorenzo Pieralisi 697936df92SLorenzo Pieralisi INIT_LIST_HEAD(&np->list); 707936df92SLorenzo Pieralisi np->iort_node = iort_node; 717936df92SLorenzo Pieralisi np->fwnode = fwnode; 727936df92SLorenzo Pieralisi 737936df92SLorenzo Pieralisi spin_lock(&iort_fwnode_lock); 747936df92SLorenzo Pieralisi list_add_tail(&np->list, &iort_fwnode_list); 757936df92SLorenzo Pieralisi spin_unlock(&iort_fwnode_lock); 767936df92SLorenzo Pieralisi 777936df92SLorenzo Pieralisi return 0; 787936df92SLorenzo Pieralisi } 797936df92SLorenzo Pieralisi 807936df92SLorenzo Pieralisi /** 817936df92SLorenzo Pieralisi * iort_get_fwnode() - Retrieve fwnode associated with an IORT node 827936df92SLorenzo Pieralisi * 837936df92SLorenzo Pieralisi * @node: IORT table node to be looked-up 847936df92SLorenzo Pieralisi * 857936df92SLorenzo Pieralisi * Returns: fwnode_handle pointer on success, NULL on failure 867936df92SLorenzo Pieralisi */ 87e3d49392SLorenzo Pieralisi static inline struct fwnode_handle *iort_get_fwnode( 88e3d49392SLorenzo Pieralisi struct acpi_iort_node *node) 897936df92SLorenzo Pieralisi { 907936df92SLorenzo Pieralisi struct iort_fwnode *curr; 917936df92SLorenzo Pieralisi struct fwnode_handle *fwnode = NULL; 927936df92SLorenzo Pieralisi 937936df92SLorenzo Pieralisi spin_lock(&iort_fwnode_lock); 947936df92SLorenzo Pieralisi list_for_each_entry(curr, &iort_fwnode_list, list) { 957936df92SLorenzo Pieralisi if (curr->iort_node == node) { 967936df92SLorenzo Pieralisi fwnode = curr->fwnode; 977936df92SLorenzo Pieralisi break; 987936df92SLorenzo Pieralisi } 997936df92SLorenzo Pieralisi } 1007936df92SLorenzo Pieralisi spin_unlock(&iort_fwnode_lock); 1017936df92SLorenzo Pieralisi 1027936df92SLorenzo Pieralisi return fwnode; 1037936df92SLorenzo Pieralisi } 1047936df92SLorenzo Pieralisi 1057936df92SLorenzo Pieralisi /** 1067936df92SLorenzo Pieralisi * iort_delete_fwnode() - Delete fwnode associated with an IORT node 1077936df92SLorenzo Pieralisi * 1087936df92SLorenzo Pieralisi * @node: IORT table node associated with fwnode to delete 1097936df92SLorenzo Pieralisi */ 1107936df92SLorenzo Pieralisi static inline void iort_delete_fwnode(struct acpi_iort_node *node) 1117936df92SLorenzo Pieralisi { 1127936df92SLorenzo Pieralisi struct iort_fwnode *curr, *tmp; 1137936df92SLorenzo Pieralisi 1147936df92SLorenzo Pieralisi spin_lock(&iort_fwnode_lock); 1157936df92SLorenzo Pieralisi list_for_each_entry_safe(curr, tmp, &iort_fwnode_list, list) { 1167936df92SLorenzo Pieralisi if (curr->iort_node == node) { 1177936df92SLorenzo Pieralisi list_del(&curr->list); 1187936df92SLorenzo Pieralisi kfree(curr); 1197936df92SLorenzo Pieralisi break; 1207936df92SLorenzo Pieralisi } 1217936df92SLorenzo Pieralisi } 1227936df92SLorenzo Pieralisi spin_unlock(&iort_fwnode_lock); 1237936df92SLorenzo Pieralisi } 1247936df92SLorenzo Pieralisi 1250a71d8b9SHanjun Guo /** 1260a71d8b9SHanjun Guo * iort_get_iort_node() - Retrieve iort_node associated with an fwnode 1270a71d8b9SHanjun Guo * 1280a71d8b9SHanjun Guo * @fwnode: fwnode associated with device to be looked-up 1290a71d8b9SHanjun Guo * 1300a71d8b9SHanjun Guo * Returns: iort_node pointer on success, NULL on failure 1310a71d8b9SHanjun Guo */ 1320a71d8b9SHanjun Guo static inline struct acpi_iort_node *iort_get_iort_node( 1330a71d8b9SHanjun Guo struct fwnode_handle *fwnode) 1340a71d8b9SHanjun Guo { 1350a71d8b9SHanjun Guo struct iort_fwnode *curr; 1360a71d8b9SHanjun Guo struct acpi_iort_node *iort_node = NULL; 1370a71d8b9SHanjun Guo 1380a71d8b9SHanjun Guo spin_lock(&iort_fwnode_lock); 1390a71d8b9SHanjun Guo list_for_each_entry(curr, &iort_fwnode_list, list) { 1400a71d8b9SHanjun Guo if (curr->fwnode == fwnode) { 1410a71d8b9SHanjun Guo iort_node = curr->iort_node; 1420a71d8b9SHanjun Guo break; 1430a71d8b9SHanjun Guo } 1440a71d8b9SHanjun Guo } 1450a71d8b9SHanjun Guo spin_unlock(&iort_fwnode_lock); 1460a71d8b9SHanjun Guo 1470a71d8b9SHanjun Guo return iort_node; 1480a71d8b9SHanjun Guo } 1490a71d8b9SHanjun Guo 15088ef16d8STomasz Nowicki typedef acpi_status (*iort_find_node_callback) 15188ef16d8STomasz Nowicki (struct acpi_iort_node *node, void *context); 15288ef16d8STomasz Nowicki 15388ef16d8STomasz Nowicki /* Root pointer to the mapped IORT table */ 15488ef16d8STomasz Nowicki static struct acpi_table_header *iort_table; 15588ef16d8STomasz Nowicki 15688ef16d8STomasz Nowicki static LIST_HEAD(iort_msi_chip_list); 15788ef16d8STomasz Nowicki static DEFINE_SPINLOCK(iort_msi_chip_lock); 15888ef16d8STomasz Nowicki 1594bf2efd2STomasz Nowicki /** 1608b4282e6SShameer Kolothum * iort_register_domain_token() - register domain token along with related 1618b4282e6SShameer Kolothum * ITS ID and base address to the list from where we can get it back later on. 1624bf2efd2STomasz Nowicki * @trans_id: ITS ID. 1638b4282e6SShameer Kolothum * @base: ITS base address. 1644bf2efd2STomasz Nowicki * @fw_node: Domain token. 1654bf2efd2STomasz Nowicki * 1664bf2efd2STomasz Nowicki * Returns: 0 on success, -ENOMEM if no memory when allocating list element 1674bf2efd2STomasz Nowicki */ 1688b4282e6SShameer Kolothum int iort_register_domain_token(int trans_id, phys_addr_t base, 1698b4282e6SShameer Kolothum struct fwnode_handle *fw_node) 1704bf2efd2STomasz Nowicki { 1714bf2efd2STomasz Nowicki struct iort_its_msi_chip *its_msi_chip; 1724bf2efd2STomasz Nowicki 1734bf2efd2STomasz Nowicki its_msi_chip = kzalloc(sizeof(*its_msi_chip), GFP_KERNEL); 1744bf2efd2STomasz Nowicki if (!its_msi_chip) 1754bf2efd2STomasz Nowicki return -ENOMEM; 1764bf2efd2STomasz Nowicki 1774bf2efd2STomasz Nowicki its_msi_chip->fw_node = fw_node; 1784bf2efd2STomasz Nowicki its_msi_chip->translation_id = trans_id; 1798b4282e6SShameer Kolothum its_msi_chip->base_addr = base; 1804bf2efd2STomasz Nowicki 1814bf2efd2STomasz Nowicki spin_lock(&iort_msi_chip_lock); 1824bf2efd2STomasz Nowicki list_add(&its_msi_chip->list, &iort_msi_chip_list); 1834bf2efd2STomasz Nowicki spin_unlock(&iort_msi_chip_lock); 1844bf2efd2STomasz Nowicki 1854bf2efd2STomasz Nowicki return 0; 1864bf2efd2STomasz Nowicki } 1874bf2efd2STomasz Nowicki 1884bf2efd2STomasz Nowicki /** 1894bf2efd2STomasz Nowicki * iort_deregister_domain_token() - Deregister domain token based on ITS ID 1904bf2efd2STomasz Nowicki * @trans_id: ITS ID. 1914bf2efd2STomasz Nowicki * 1924bf2efd2STomasz Nowicki * Returns: none. 1934bf2efd2STomasz Nowicki */ 1944bf2efd2STomasz Nowicki void iort_deregister_domain_token(int trans_id) 1954bf2efd2STomasz Nowicki { 1964bf2efd2STomasz Nowicki struct iort_its_msi_chip *its_msi_chip, *t; 1974bf2efd2STomasz Nowicki 1984bf2efd2STomasz Nowicki spin_lock(&iort_msi_chip_lock); 1994bf2efd2STomasz Nowicki list_for_each_entry_safe(its_msi_chip, t, &iort_msi_chip_list, list) { 2004bf2efd2STomasz Nowicki if (its_msi_chip->translation_id == trans_id) { 2014bf2efd2STomasz Nowicki list_del(&its_msi_chip->list); 2024bf2efd2STomasz Nowicki kfree(its_msi_chip); 2034bf2efd2STomasz Nowicki break; 2044bf2efd2STomasz Nowicki } 2054bf2efd2STomasz Nowicki } 2064bf2efd2STomasz Nowicki spin_unlock(&iort_msi_chip_lock); 2074bf2efd2STomasz Nowicki } 2084bf2efd2STomasz Nowicki 2094bf2efd2STomasz Nowicki /** 2104bf2efd2STomasz Nowicki * iort_find_domain_token() - Find domain token based on given ITS ID 2114bf2efd2STomasz Nowicki * @trans_id: ITS ID. 2124bf2efd2STomasz Nowicki * 2134bf2efd2STomasz Nowicki * Returns: domain token when find on the list, NULL otherwise 2144bf2efd2STomasz Nowicki */ 2154bf2efd2STomasz Nowicki struct fwnode_handle *iort_find_domain_token(int trans_id) 2164bf2efd2STomasz Nowicki { 2174bf2efd2STomasz Nowicki struct fwnode_handle *fw_node = NULL; 2184bf2efd2STomasz Nowicki struct iort_its_msi_chip *its_msi_chip; 2194bf2efd2STomasz Nowicki 2204bf2efd2STomasz Nowicki spin_lock(&iort_msi_chip_lock); 2214bf2efd2STomasz Nowicki list_for_each_entry(its_msi_chip, &iort_msi_chip_list, list) { 2224bf2efd2STomasz Nowicki if (its_msi_chip->translation_id == trans_id) { 2234bf2efd2STomasz Nowicki fw_node = its_msi_chip->fw_node; 2244bf2efd2STomasz Nowicki break; 2254bf2efd2STomasz Nowicki } 2264bf2efd2STomasz Nowicki } 2274bf2efd2STomasz Nowicki spin_unlock(&iort_msi_chip_lock); 2284bf2efd2STomasz Nowicki 2294bf2efd2STomasz Nowicki return fw_node; 2304bf2efd2STomasz Nowicki } 2314bf2efd2STomasz Nowicki 23288ef16d8STomasz Nowicki static struct acpi_iort_node *iort_scan_node(enum acpi_iort_node_type type, 23388ef16d8STomasz Nowicki iort_find_node_callback callback, 23488ef16d8STomasz Nowicki void *context) 23588ef16d8STomasz Nowicki { 23688ef16d8STomasz Nowicki struct acpi_iort_node *iort_node, *iort_end; 23788ef16d8STomasz Nowicki struct acpi_table_iort *iort; 23888ef16d8STomasz Nowicki int i; 23988ef16d8STomasz Nowicki 24088ef16d8STomasz Nowicki if (!iort_table) 24188ef16d8STomasz Nowicki return NULL; 24288ef16d8STomasz Nowicki 24388ef16d8STomasz Nowicki /* Get the first IORT node */ 24488ef16d8STomasz Nowicki iort = (struct acpi_table_iort *)iort_table; 24588ef16d8STomasz Nowicki iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort, 24688ef16d8STomasz Nowicki iort->node_offset); 24788ef16d8STomasz Nowicki iort_end = ACPI_ADD_PTR(struct acpi_iort_node, iort_table, 24888ef16d8STomasz Nowicki iort_table->length); 24988ef16d8STomasz Nowicki 25088ef16d8STomasz Nowicki for (i = 0; i < iort->node_count; i++) { 25188ef16d8STomasz Nowicki if (WARN_TAINT(iort_node >= iort_end, TAINT_FIRMWARE_WORKAROUND, 25288ef16d8STomasz Nowicki "IORT node pointer overflows, bad table!\n")) 25388ef16d8STomasz Nowicki return NULL; 25488ef16d8STomasz Nowicki 25588ef16d8STomasz Nowicki if (iort_node->type == type && 25688ef16d8STomasz Nowicki ACPI_SUCCESS(callback(iort_node, context))) 25788ef16d8STomasz Nowicki return iort_node; 25888ef16d8STomasz Nowicki 25988ef16d8STomasz Nowicki iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort_node, 26088ef16d8STomasz Nowicki iort_node->length); 26188ef16d8STomasz Nowicki } 26288ef16d8STomasz Nowicki 26388ef16d8STomasz Nowicki return NULL; 26488ef16d8STomasz Nowicki } 26588ef16d8STomasz Nowicki 26688ef16d8STomasz Nowicki static acpi_status iort_match_node_callback(struct acpi_iort_node *node, 26788ef16d8STomasz Nowicki void *context) 26888ef16d8STomasz Nowicki { 26988ef16d8STomasz Nowicki struct device *dev = context; 270c92bdfe8SHanjun Guo acpi_status status = AE_NOT_FOUND; 27188ef16d8STomasz Nowicki 27288ef16d8STomasz Nowicki if (node->type == ACPI_IORT_NODE_NAMED_COMPONENT) { 27388ef16d8STomasz Nowicki struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL }; 27488ef16d8STomasz Nowicki struct acpi_device *adev = to_acpi_device_node(dev->fwnode); 27588ef16d8STomasz Nowicki struct acpi_iort_named_component *ncomp; 27688ef16d8STomasz Nowicki 277c92bdfe8SHanjun Guo if (!adev) 27888ef16d8STomasz Nowicki goto out; 27988ef16d8STomasz Nowicki 28088ef16d8STomasz Nowicki status = acpi_get_name(adev->handle, ACPI_FULL_PATHNAME, &buf); 28188ef16d8STomasz Nowicki if (ACPI_FAILURE(status)) { 28288ef16d8STomasz Nowicki dev_warn(dev, "Can't get device full path name\n"); 28388ef16d8STomasz Nowicki goto out; 28488ef16d8STomasz Nowicki } 28588ef16d8STomasz Nowicki 28688ef16d8STomasz Nowicki ncomp = (struct acpi_iort_named_component *)node->node_data; 28788ef16d8STomasz Nowicki status = !strcmp(ncomp->device_name, buf.pointer) ? 28888ef16d8STomasz Nowicki AE_OK : AE_NOT_FOUND; 28988ef16d8STomasz Nowicki acpi_os_free(buf.pointer); 29088ef16d8STomasz Nowicki } else if (node->type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX) { 29188ef16d8STomasz Nowicki struct acpi_iort_root_complex *pci_rc; 29288ef16d8STomasz Nowicki struct pci_bus *bus; 29388ef16d8STomasz Nowicki 29488ef16d8STomasz Nowicki bus = to_pci_bus(dev); 29588ef16d8STomasz Nowicki pci_rc = (struct acpi_iort_root_complex *)node->node_data; 29688ef16d8STomasz Nowicki 29788ef16d8STomasz Nowicki /* 29888ef16d8STomasz Nowicki * It is assumed that PCI segment numbers maps one-to-one 29988ef16d8STomasz Nowicki * with root complexes. Each segment number can represent only 30088ef16d8STomasz Nowicki * one root complex. 30188ef16d8STomasz Nowicki */ 30288ef16d8STomasz Nowicki status = pci_rc->pci_segment_number == pci_domain_nr(bus) ? 30388ef16d8STomasz Nowicki AE_OK : AE_NOT_FOUND; 30488ef16d8STomasz Nowicki } 30588ef16d8STomasz Nowicki out: 30688ef16d8STomasz Nowicki return status; 30788ef16d8STomasz Nowicki } 30888ef16d8STomasz Nowicki 30988ef16d8STomasz Nowicki static int iort_id_map(struct acpi_iort_id_mapping *map, u8 type, u32 rid_in, 31088ef16d8STomasz Nowicki u32 *rid_out) 31188ef16d8STomasz Nowicki { 31288ef16d8STomasz Nowicki /* Single mapping does not care for input id */ 31388ef16d8STomasz Nowicki if (map->flags & ACPI_IORT_ID_SINGLE_MAPPING) { 31488ef16d8STomasz Nowicki if (type == ACPI_IORT_NODE_NAMED_COMPONENT || 31588ef16d8STomasz Nowicki type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX) { 31688ef16d8STomasz Nowicki *rid_out = map->output_base; 31788ef16d8STomasz Nowicki return 0; 31888ef16d8STomasz Nowicki } 31988ef16d8STomasz Nowicki 32088ef16d8STomasz Nowicki pr_warn(FW_BUG "[map %p] SINGLE MAPPING flag not allowed for node type %d, skipping ID map\n", 32188ef16d8STomasz Nowicki map, type); 32288ef16d8STomasz Nowicki return -ENXIO; 32388ef16d8STomasz Nowicki } 32488ef16d8STomasz Nowicki 32588ef16d8STomasz Nowicki if (rid_in < map->input_base || 32688ef16d8STomasz Nowicki (rid_in >= map->input_base + map->id_count)) 32788ef16d8STomasz Nowicki return -ENXIO; 32888ef16d8STomasz Nowicki 32988ef16d8STomasz Nowicki *rid_out = map->output_base + (rid_in - map->input_base); 33088ef16d8STomasz Nowicki return 0; 33188ef16d8STomasz Nowicki } 33288ef16d8STomasz Nowicki 333e3d49392SLorenzo Pieralisi static struct acpi_iort_node *iort_node_get_id(struct acpi_iort_node *node, 3348ca4f1d3SHanjun Guo u32 *id_out, int index) 335618f535aSLorenzo Pieralisi { 336618f535aSLorenzo Pieralisi struct acpi_iort_node *parent; 337618f535aSLorenzo Pieralisi struct acpi_iort_id_mapping *map; 338618f535aSLorenzo Pieralisi 339618f535aSLorenzo Pieralisi if (!node->mapping_offset || !node->mapping_count || 340618f535aSLorenzo Pieralisi index >= node->mapping_count) 341618f535aSLorenzo Pieralisi return NULL; 342618f535aSLorenzo Pieralisi 343618f535aSLorenzo Pieralisi map = ACPI_ADD_PTR(struct acpi_iort_id_mapping, node, 344030abd8aSLorenzo Pieralisi node->mapping_offset + index * sizeof(*map)); 345618f535aSLorenzo Pieralisi 346618f535aSLorenzo Pieralisi /* Firmware bug! */ 347618f535aSLorenzo Pieralisi if (!map->output_reference) { 348618f535aSLorenzo Pieralisi pr_err(FW_BUG "[node %p type %d] ID map has NULL parent reference\n", 349618f535aSLorenzo Pieralisi node, node->type); 350618f535aSLorenzo Pieralisi return NULL; 351618f535aSLorenzo Pieralisi } 352618f535aSLorenzo Pieralisi 353618f535aSLorenzo Pieralisi parent = ACPI_ADD_PTR(struct acpi_iort_node, iort_table, 354618f535aSLorenzo Pieralisi map->output_reference); 355618f535aSLorenzo Pieralisi 356030abd8aSLorenzo Pieralisi if (map->flags & ACPI_IORT_ID_SINGLE_MAPPING) { 357618f535aSLorenzo Pieralisi if (node->type == ACPI_IORT_NODE_NAMED_COMPONENT || 35886456a3fSHanjun Guo node->type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX || 35986456a3fSHanjun Guo node->type == ACPI_IORT_NODE_SMMU_V3) { 360030abd8aSLorenzo Pieralisi *id_out = map->output_base; 361618f535aSLorenzo Pieralisi return parent; 362618f535aSLorenzo Pieralisi } 363618f535aSLorenzo Pieralisi } 364618f535aSLorenzo Pieralisi 365618f535aSLorenzo Pieralisi return NULL; 366618f535aSLorenzo Pieralisi } 367618f535aSLorenzo Pieralisi 36886456a3fSHanjun Guo static int iort_get_id_mapping_index(struct acpi_iort_node *node) 36986456a3fSHanjun Guo { 37086456a3fSHanjun Guo struct acpi_iort_smmu_v3 *smmu; 37186456a3fSHanjun Guo 37286456a3fSHanjun Guo switch (node->type) { 37386456a3fSHanjun Guo case ACPI_IORT_NODE_SMMU_V3: 37486456a3fSHanjun Guo /* 37586456a3fSHanjun Guo * SMMUv3 dev ID mapping index was introduced in revision 1 37686456a3fSHanjun Guo * table, not available in revision 0 37786456a3fSHanjun Guo */ 37886456a3fSHanjun Guo if (node->revision < 1) 37986456a3fSHanjun Guo return -EINVAL; 38086456a3fSHanjun Guo 38186456a3fSHanjun Guo smmu = (struct acpi_iort_smmu_v3 *)node->node_data; 38286456a3fSHanjun Guo /* 38386456a3fSHanjun Guo * ID mapping index is only ignored if all interrupts are 38486456a3fSHanjun Guo * GSIV based 38586456a3fSHanjun Guo */ 38686456a3fSHanjun Guo if (smmu->event_gsiv && smmu->pri_gsiv && smmu->gerr_gsiv 38786456a3fSHanjun Guo && smmu->sync_gsiv) 38886456a3fSHanjun Guo return -EINVAL; 38986456a3fSHanjun Guo 39086456a3fSHanjun Guo if (smmu->id_mapping_index >= node->mapping_count) { 39186456a3fSHanjun Guo pr_err(FW_BUG "[node %p type %d] ID mapping index overflows valid mappings\n", 39286456a3fSHanjun Guo node, node->type); 39386456a3fSHanjun Guo return -EINVAL; 39486456a3fSHanjun Guo } 39586456a3fSHanjun Guo 39686456a3fSHanjun Guo return smmu->id_mapping_index; 39786456a3fSHanjun Guo default: 39886456a3fSHanjun Guo return -EINVAL; 39986456a3fSHanjun Guo } 40086456a3fSHanjun Guo } 4018c8df8dcSHanjun Guo 402697f6093SHanjun Guo static struct acpi_iort_node *iort_node_map_id(struct acpi_iort_node *node, 403697f6093SHanjun Guo u32 id_in, u32 *id_out, 404ea50b524SLorenzo Pieralisi u8 type_mask) 40588ef16d8STomasz Nowicki { 406697f6093SHanjun Guo u32 id = id_in; 40788ef16d8STomasz Nowicki 40888ef16d8STomasz Nowicki /* Parse the ID mapping tree to find specified node type */ 40988ef16d8STomasz Nowicki while (node) { 41088ef16d8STomasz Nowicki struct acpi_iort_id_mapping *map; 4118c8df8dcSHanjun Guo int i, index; 41288ef16d8STomasz Nowicki 413ea50b524SLorenzo Pieralisi if (IORT_TYPE_MASK(node->type) & type_mask) { 414697f6093SHanjun Guo if (id_out) 415697f6093SHanjun Guo *id_out = id; 41688ef16d8STomasz Nowicki return node; 41788ef16d8STomasz Nowicki } 41888ef16d8STomasz Nowicki 41988ef16d8STomasz Nowicki if (!node->mapping_offset || !node->mapping_count) 42088ef16d8STomasz Nowicki goto fail_map; 42188ef16d8STomasz Nowicki 42288ef16d8STomasz Nowicki map = ACPI_ADD_PTR(struct acpi_iort_id_mapping, node, 42388ef16d8STomasz Nowicki node->mapping_offset); 42488ef16d8STomasz Nowicki 42588ef16d8STomasz Nowicki /* Firmware bug! */ 42688ef16d8STomasz Nowicki if (!map->output_reference) { 42788ef16d8STomasz Nowicki pr_err(FW_BUG "[node %p type %d] ID map has NULL parent reference\n", 42888ef16d8STomasz Nowicki node, node->type); 42988ef16d8STomasz Nowicki goto fail_map; 43088ef16d8STomasz Nowicki } 43188ef16d8STomasz Nowicki 4328c8df8dcSHanjun Guo /* 4338c8df8dcSHanjun Guo * Get the special ID mapping index (if any) and skip its 4348c8df8dcSHanjun Guo * associated ID map to prevent erroneous multi-stage 4358c8df8dcSHanjun Guo * IORT ID translations. 4368c8df8dcSHanjun Guo */ 4378c8df8dcSHanjun Guo index = iort_get_id_mapping_index(node); 4388c8df8dcSHanjun Guo 439697f6093SHanjun Guo /* Do the ID translation */ 44088ef16d8STomasz Nowicki for (i = 0; i < node->mapping_count; i++, map++) { 4418c8df8dcSHanjun Guo /* if it is special mapping index, skip it */ 4428c8df8dcSHanjun Guo if (i == index) 4438c8df8dcSHanjun Guo continue; 4448c8df8dcSHanjun Guo 445697f6093SHanjun Guo if (!iort_id_map(map, node->type, id, &id)) 44688ef16d8STomasz Nowicki break; 44788ef16d8STomasz Nowicki } 44888ef16d8STomasz Nowicki 44988ef16d8STomasz Nowicki if (i == node->mapping_count) 45088ef16d8STomasz Nowicki goto fail_map; 45188ef16d8STomasz Nowicki 45288ef16d8STomasz Nowicki node = ACPI_ADD_PTR(struct acpi_iort_node, iort_table, 45388ef16d8STomasz Nowicki map->output_reference); 45488ef16d8STomasz Nowicki } 45588ef16d8STomasz Nowicki 45688ef16d8STomasz Nowicki fail_map: 457697f6093SHanjun Guo /* Map input ID to output ID unchanged on mapping failure */ 458697f6093SHanjun Guo if (id_out) 459697f6093SHanjun Guo *id_out = id_in; 46088ef16d8STomasz Nowicki 46188ef16d8STomasz Nowicki return NULL; 46288ef16d8STomasz Nowicki } 46388ef16d8STomasz Nowicki 464e3d49392SLorenzo Pieralisi static struct acpi_iort_node *iort_node_map_platform_id( 465e3d49392SLorenzo Pieralisi struct acpi_iort_node *node, u32 *id_out, u8 type_mask, 4668ca4f1d3SHanjun Guo int index) 4678ca4f1d3SHanjun Guo { 4688ca4f1d3SHanjun Guo struct acpi_iort_node *parent; 4698ca4f1d3SHanjun Guo u32 id; 4708ca4f1d3SHanjun Guo 4718ca4f1d3SHanjun Guo /* step 1: retrieve the initial dev id */ 4728ca4f1d3SHanjun Guo parent = iort_node_get_id(node, &id, index); 4738ca4f1d3SHanjun Guo if (!parent) 4748ca4f1d3SHanjun Guo return NULL; 4758ca4f1d3SHanjun Guo 4768ca4f1d3SHanjun Guo /* 4778ca4f1d3SHanjun Guo * optional step 2: map the initial dev id if its parent is not 4788ca4f1d3SHanjun Guo * the target type we want, map it again for the use cases such 4798ca4f1d3SHanjun Guo * as NC (named component) -> SMMU -> ITS. If the type is matched, 4808ca4f1d3SHanjun Guo * return the initial dev id and its parent pointer directly. 4818ca4f1d3SHanjun Guo */ 4828ca4f1d3SHanjun Guo if (!(IORT_TYPE_MASK(parent->type) & type_mask)) 4838ca4f1d3SHanjun Guo parent = iort_node_map_id(parent, id, id_out, type_mask); 4848ca4f1d3SHanjun Guo else 4858ca4f1d3SHanjun Guo if (id_out) 4868ca4f1d3SHanjun Guo *id_out = id; 4878ca4f1d3SHanjun Guo 4888ca4f1d3SHanjun Guo return parent; 4898ca4f1d3SHanjun Guo } 4908ca4f1d3SHanjun Guo 49188ef16d8STomasz Nowicki static struct acpi_iort_node *iort_find_dev_node(struct device *dev) 49288ef16d8STomasz Nowicki { 49388ef16d8STomasz Nowicki struct pci_bus *pbus; 49488ef16d8STomasz Nowicki 4950a71d8b9SHanjun Guo if (!dev_is_pci(dev)) { 4960a71d8b9SHanjun Guo struct acpi_iort_node *node; 4970a71d8b9SHanjun Guo /* 4980a71d8b9SHanjun Guo * scan iort_fwnode_list to see if it's an iort platform 4990a71d8b9SHanjun Guo * device (such as SMMU, PMCG),its iort node already cached 5000a71d8b9SHanjun Guo * and associated with fwnode when iort platform devices 5010a71d8b9SHanjun Guo * were initialized. 5020a71d8b9SHanjun Guo */ 5030a71d8b9SHanjun Guo node = iort_get_iort_node(dev->fwnode); 5040a71d8b9SHanjun Guo if (node) 5050a71d8b9SHanjun Guo return node; 5060a71d8b9SHanjun Guo 5070a71d8b9SHanjun Guo /* 5080a71d8b9SHanjun Guo * if not, then it should be a platform device defined in 5090a71d8b9SHanjun Guo * DSDT/SSDT (with Named Component node in IORT) 5100a71d8b9SHanjun Guo */ 51188ef16d8STomasz Nowicki return iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT, 51288ef16d8STomasz Nowicki iort_match_node_callback, dev); 5130a71d8b9SHanjun Guo } 51488ef16d8STomasz Nowicki 51588ef16d8STomasz Nowicki /* Find a PCI root bus */ 51688ef16d8STomasz Nowicki pbus = to_pci_dev(dev)->bus; 51788ef16d8STomasz Nowicki while (!pci_is_root_bus(pbus)) 51888ef16d8STomasz Nowicki pbus = pbus->parent; 51988ef16d8STomasz Nowicki 52088ef16d8STomasz Nowicki return iort_scan_node(ACPI_IORT_NODE_PCI_ROOT_COMPLEX, 52188ef16d8STomasz Nowicki iort_match_node_callback, &pbus->dev); 52288ef16d8STomasz Nowicki } 52388ef16d8STomasz Nowicki 5244bf2efd2STomasz Nowicki /** 5254bf2efd2STomasz Nowicki * iort_msi_map_rid() - Map a MSI requester ID for a device 5264bf2efd2STomasz Nowicki * @dev: The device for which the mapping is to be done. 5274bf2efd2STomasz Nowicki * @req_id: The device requester ID. 5284bf2efd2STomasz Nowicki * 5294bf2efd2STomasz Nowicki * Returns: mapped MSI RID on success, input requester ID otherwise 5304bf2efd2STomasz Nowicki */ 5314bf2efd2STomasz Nowicki u32 iort_msi_map_rid(struct device *dev, u32 req_id) 5324bf2efd2STomasz Nowicki { 5334bf2efd2STomasz Nowicki struct acpi_iort_node *node; 5344bf2efd2STomasz Nowicki u32 dev_id; 5354bf2efd2STomasz Nowicki 5364bf2efd2STomasz Nowicki node = iort_find_dev_node(dev); 5374bf2efd2STomasz Nowicki if (!node) 5384bf2efd2STomasz Nowicki return req_id; 5394bf2efd2STomasz Nowicki 540697f6093SHanjun Guo iort_node_map_id(node, req_id, &dev_id, IORT_MSI_TYPE); 5414bf2efd2STomasz Nowicki return dev_id; 5424bf2efd2STomasz Nowicki } 5434bf2efd2STomasz Nowicki 5444bf2efd2STomasz Nowicki /** 545ae7c1838SHanjun Guo * iort_pmsi_get_dev_id() - Get the device id for a device 546ae7c1838SHanjun Guo * @dev: The device for which the mapping is to be done. 547ae7c1838SHanjun Guo * @dev_id: The device ID found. 548ae7c1838SHanjun Guo * 549ae7c1838SHanjun Guo * Returns: 0 for successful find a dev id, -ENODEV on error 550ae7c1838SHanjun Guo */ 551ae7c1838SHanjun Guo int iort_pmsi_get_dev_id(struct device *dev, u32 *dev_id) 552ae7c1838SHanjun Guo { 5538c8df8dcSHanjun Guo int i, index; 554ae7c1838SHanjun Guo struct acpi_iort_node *node; 555ae7c1838SHanjun Guo 556ae7c1838SHanjun Guo node = iort_find_dev_node(dev); 557ae7c1838SHanjun Guo if (!node) 558ae7c1838SHanjun Guo return -ENODEV; 559ae7c1838SHanjun Guo 5608c8df8dcSHanjun Guo index = iort_get_id_mapping_index(node); 5618c8df8dcSHanjun Guo /* if there is a valid index, go get the dev_id directly */ 5628c8df8dcSHanjun Guo if (index >= 0) { 5638c8df8dcSHanjun Guo if (iort_node_get_id(node, dev_id, index)) 564ae7c1838SHanjun Guo return 0; 5658c8df8dcSHanjun Guo } else { 5668c8df8dcSHanjun Guo for (i = 0; i < node->mapping_count; i++) { 5678c8df8dcSHanjun Guo if (iort_node_map_platform_id(node, dev_id, 5688c8df8dcSHanjun Guo IORT_MSI_TYPE, i)) 5698c8df8dcSHanjun Guo return 0; 5708c8df8dcSHanjun Guo } 571ae7c1838SHanjun Guo } 572ae7c1838SHanjun Guo 573ae7c1838SHanjun Guo return -ENODEV; 574ae7c1838SHanjun Guo } 575ae7c1838SHanjun Guo 5768b4282e6SShameer Kolothum static int __maybe_unused iort_find_its_base(u32 its_id, phys_addr_t *base) 5778b4282e6SShameer Kolothum { 5788b4282e6SShameer Kolothum struct iort_its_msi_chip *its_msi_chip; 5798b4282e6SShameer Kolothum int ret = -ENODEV; 5808b4282e6SShameer Kolothum 5818b4282e6SShameer Kolothum spin_lock(&iort_msi_chip_lock); 5828b4282e6SShameer Kolothum list_for_each_entry(its_msi_chip, &iort_msi_chip_list, list) { 5838b4282e6SShameer Kolothum if (its_msi_chip->translation_id == its_id) { 5848b4282e6SShameer Kolothum *base = its_msi_chip->base_addr; 5858b4282e6SShameer Kolothum ret = 0; 5868b4282e6SShameer Kolothum break; 5878b4282e6SShameer Kolothum } 5888b4282e6SShameer Kolothum } 5898b4282e6SShameer Kolothum spin_unlock(&iort_msi_chip_lock); 5908b4282e6SShameer Kolothum 5918b4282e6SShameer Kolothum return ret; 5928b4282e6SShameer Kolothum } 5938b4282e6SShameer Kolothum 594ae7c1838SHanjun Guo /** 5954bf2efd2STomasz Nowicki * iort_dev_find_its_id() - Find the ITS identifier for a device 5964bf2efd2STomasz Nowicki * @dev: The device. 5976cb6bf56SHanjun Guo * @req_id: Device's requester ID 5984bf2efd2STomasz Nowicki * @idx: Index of the ITS identifier list. 5994bf2efd2STomasz Nowicki * @its_id: ITS identifier. 6004bf2efd2STomasz Nowicki * 6014bf2efd2STomasz Nowicki * Returns: 0 on success, appropriate error value otherwise 6024bf2efd2STomasz Nowicki */ 6034bf2efd2STomasz Nowicki static int iort_dev_find_its_id(struct device *dev, u32 req_id, 6044bf2efd2STomasz Nowicki unsigned int idx, int *its_id) 6054bf2efd2STomasz Nowicki { 6064bf2efd2STomasz Nowicki struct acpi_iort_its_group *its; 6074bf2efd2STomasz Nowicki struct acpi_iort_node *node; 6084bf2efd2STomasz Nowicki 6094bf2efd2STomasz Nowicki node = iort_find_dev_node(dev); 6104bf2efd2STomasz Nowicki if (!node) 6114bf2efd2STomasz Nowicki return -ENXIO; 6124bf2efd2STomasz Nowicki 613697f6093SHanjun Guo node = iort_node_map_id(node, req_id, NULL, IORT_MSI_TYPE); 6144bf2efd2STomasz Nowicki if (!node) 6154bf2efd2STomasz Nowicki return -ENXIO; 6164bf2efd2STomasz Nowicki 6174bf2efd2STomasz Nowicki /* Move to ITS specific data */ 6184bf2efd2STomasz Nowicki its = (struct acpi_iort_its_group *)node->node_data; 6194bf2efd2STomasz Nowicki if (idx > its->its_count) { 6204bf2efd2STomasz Nowicki dev_err(dev, "requested ITS ID index [%d] is greater than available [%d]\n", 6214bf2efd2STomasz Nowicki idx, its->its_count); 6224bf2efd2STomasz Nowicki return -ENXIO; 6234bf2efd2STomasz Nowicki } 6244bf2efd2STomasz Nowicki 6254bf2efd2STomasz Nowicki *its_id = its->identifiers[idx]; 6264bf2efd2STomasz Nowicki return 0; 6274bf2efd2STomasz Nowicki } 6284bf2efd2STomasz Nowicki 6294bf2efd2STomasz Nowicki /** 6304bf2efd2STomasz Nowicki * iort_get_device_domain() - Find MSI domain related to a device 6314bf2efd2STomasz Nowicki * @dev: The device. 6324bf2efd2STomasz Nowicki * @req_id: Requester ID for the device. 6334bf2efd2STomasz Nowicki * 6344bf2efd2STomasz Nowicki * Returns: the MSI domain for this device, NULL otherwise 6354bf2efd2STomasz Nowicki */ 6364bf2efd2STomasz Nowicki struct irq_domain *iort_get_device_domain(struct device *dev, u32 req_id) 6374bf2efd2STomasz Nowicki { 6384bf2efd2STomasz Nowicki struct fwnode_handle *handle; 6394bf2efd2STomasz Nowicki int its_id; 6404bf2efd2STomasz Nowicki 6414bf2efd2STomasz Nowicki if (iort_dev_find_its_id(dev, req_id, 0, &its_id)) 6424bf2efd2STomasz Nowicki return NULL; 6434bf2efd2STomasz Nowicki 6444bf2efd2STomasz Nowicki handle = iort_find_domain_token(its_id); 6454bf2efd2STomasz Nowicki if (!handle) 6464bf2efd2STomasz Nowicki return NULL; 6474bf2efd2STomasz Nowicki 6484bf2efd2STomasz Nowicki return irq_find_matching_fwnode(handle, DOMAIN_BUS_PCI_MSI); 6494bf2efd2STomasz Nowicki } 6504bf2efd2STomasz Nowicki 65165637901SLorenzo Pieralisi static void iort_set_device_domain(struct device *dev, 65265637901SLorenzo Pieralisi struct acpi_iort_node *node) 65365637901SLorenzo Pieralisi { 65465637901SLorenzo Pieralisi struct acpi_iort_its_group *its; 65565637901SLorenzo Pieralisi struct acpi_iort_node *msi_parent; 65665637901SLorenzo Pieralisi struct acpi_iort_id_mapping *map; 65765637901SLorenzo Pieralisi struct fwnode_handle *iort_fwnode; 65865637901SLorenzo Pieralisi struct irq_domain *domain; 65965637901SLorenzo Pieralisi int index; 66065637901SLorenzo Pieralisi 66165637901SLorenzo Pieralisi index = iort_get_id_mapping_index(node); 66265637901SLorenzo Pieralisi if (index < 0) 66365637901SLorenzo Pieralisi return; 66465637901SLorenzo Pieralisi 66565637901SLorenzo Pieralisi map = ACPI_ADD_PTR(struct acpi_iort_id_mapping, node, 66665637901SLorenzo Pieralisi node->mapping_offset + index * sizeof(*map)); 66765637901SLorenzo Pieralisi 66865637901SLorenzo Pieralisi /* Firmware bug! */ 66965637901SLorenzo Pieralisi if (!map->output_reference || 67065637901SLorenzo Pieralisi !(map->flags & ACPI_IORT_ID_SINGLE_MAPPING)) { 67165637901SLorenzo Pieralisi pr_err(FW_BUG "[node %p type %d] Invalid MSI mapping\n", 67265637901SLorenzo Pieralisi node, node->type); 67365637901SLorenzo Pieralisi return; 67465637901SLorenzo Pieralisi } 67565637901SLorenzo Pieralisi 67665637901SLorenzo Pieralisi msi_parent = ACPI_ADD_PTR(struct acpi_iort_node, iort_table, 67765637901SLorenzo Pieralisi map->output_reference); 67865637901SLorenzo Pieralisi 67965637901SLorenzo Pieralisi if (!msi_parent || msi_parent->type != ACPI_IORT_NODE_ITS_GROUP) 68065637901SLorenzo Pieralisi return; 68165637901SLorenzo Pieralisi 68265637901SLorenzo Pieralisi /* Move to ITS specific data */ 68365637901SLorenzo Pieralisi its = (struct acpi_iort_its_group *)msi_parent->node_data; 68465637901SLorenzo Pieralisi 68565637901SLorenzo Pieralisi iort_fwnode = iort_find_domain_token(its->identifiers[0]); 68665637901SLorenzo Pieralisi if (!iort_fwnode) 68765637901SLorenzo Pieralisi return; 68865637901SLorenzo Pieralisi 68965637901SLorenzo Pieralisi domain = irq_find_matching_fwnode(iort_fwnode, DOMAIN_BUS_PLATFORM_MSI); 69065637901SLorenzo Pieralisi if (domain) 69165637901SLorenzo Pieralisi dev_set_msi_domain(dev, domain); 69265637901SLorenzo Pieralisi } 69365637901SLorenzo Pieralisi 694d4f54a18SHanjun Guo /** 695d4f54a18SHanjun Guo * iort_get_platform_device_domain() - Find MSI domain related to a 696d4f54a18SHanjun Guo * platform device 697d4f54a18SHanjun Guo * @dev: the dev pointer associated with the platform device 698d4f54a18SHanjun Guo * 699d4f54a18SHanjun Guo * Returns: the MSI domain for this device, NULL otherwise 700d4f54a18SHanjun Guo */ 701d4f54a18SHanjun Guo static struct irq_domain *iort_get_platform_device_domain(struct device *dev) 702d4f54a18SHanjun Guo { 703ea2412dcSLorenzo Pieralisi struct acpi_iort_node *node, *msi_parent = NULL; 704d4f54a18SHanjun Guo struct fwnode_handle *iort_fwnode; 705d4f54a18SHanjun Guo struct acpi_iort_its_group *its; 706d4f54a18SHanjun Guo int i; 707d4f54a18SHanjun Guo 708d4f54a18SHanjun Guo /* find its associated iort node */ 709d4f54a18SHanjun Guo node = iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT, 710d4f54a18SHanjun Guo iort_match_node_callback, dev); 711d4f54a18SHanjun Guo if (!node) 712d4f54a18SHanjun Guo return NULL; 713d4f54a18SHanjun Guo 714d4f54a18SHanjun Guo /* then find its msi parent node */ 715d4f54a18SHanjun Guo for (i = 0; i < node->mapping_count; i++) { 716d4f54a18SHanjun Guo msi_parent = iort_node_map_platform_id(node, NULL, 717d4f54a18SHanjun Guo IORT_MSI_TYPE, i); 718d4f54a18SHanjun Guo if (msi_parent) 719d4f54a18SHanjun Guo break; 720d4f54a18SHanjun Guo } 721d4f54a18SHanjun Guo 722d4f54a18SHanjun Guo if (!msi_parent) 723d4f54a18SHanjun Guo return NULL; 724d4f54a18SHanjun Guo 725d4f54a18SHanjun Guo /* Move to ITS specific data */ 726d4f54a18SHanjun Guo its = (struct acpi_iort_its_group *)msi_parent->node_data; 727d4f54a18SHanjun Guo 728d4f54a18SHanjun Guo iort_fwnode = iort_find_domain_token(its->identifiers[0]); 729d4f54a18SHanjun Guo if (!iort_fwnode) 730d4f54a18SHanjun Guo return NULL; 731d4f54a18SHanjun Guo 732d4f54a18SHanjun Guo return irq_find_matching_fwnode(iort_fwnode, DOMAIN_BUS_PLATFORM_MSI); 733d4f54a18SHanjun Guo } 734d4f54a18SHanjun Guo 735d4f54a18SHanjun Guo void acpi_configure_pmsi_domain(struct device *dev) 736d4f54a18SHanjun Guo { 737d4f54a18SHanjun Guo struct irq_domain *msi_domain; 738d4f54a18SHanjun Guo 739d4f54a18SHanjun Guo msi_domain = iort_get_platform_device_domain(dev); 740d4f54a18SHanjun Guo if (msi_domain) 741d4f54a18SHanjun Guo dev_set_msi_domain(dev, msi_domain); 742d4f54a18SHanjun Guo } 743d4f54a18SHanjun Guo 744bc8648d4SRobin Murphy static int __maybe_unused __get_pci_rid(struct pci_dev *pdev, u16 alias, 745bc8648d4SRobin Murphy void *data) 746643b8e4dSLorenzo Pieralisi { 747643b8e4dSLorenzo Pieralisi u32 *rid = data; 748643b8e4dSLorenzo Pieralisi 749643b8e4dSLorenzo Pieralisi *rid = alias; 750643b8e4dSLorenzo Pieralisi return 0; 751643b8e4dSLorenzo Pieralisi } 752643b8e4dSLorenzo Pieralisi 753643b8e4dSLorenzo Pieralisi static int arm_smmu_iort_xlate(struct device *dev, u32 streamid, 754643b8e4dSLorenzo Pieralisi struct fwnode_handle *fwnode, 755643b8e4dSLorenzo Pieralisi const struct iommu_ops *ops) 756643b8e4dSLorenzo Pieralisi { 757643b8e4dSLorenzo Pieralisi int ret = iommu_fwspec_init(dev, fwnode, ops); 758643b8e4dSLorenzo Pieralisi 759643b8e4dSLorenzo Pieralisi if (!ret) 760643b8e4dSLorenzo Pieralisi ret = iommu_fwspec_add_ids(dev, &streamid, 1); 761643b8e4dSLorenzo Pieralisi 762643b8e4dSLorenzo Pieralisi return ret; 763643b8e4dSLorenzo Pieralisi } 764643b8e4dSLorenzo Pieralisi 7651d9029d4SLorenzo Pieralisi static inline bool iort_iommu_driver_enabled(u8 type) 7661d9029d4SLorenzo Pieralisi { 7671d9029d4SLorenzo Pieralisi switch (type) { 7681d9029d4SLorenzo Pieralisi case ACPI_IORT_NODE_SMMU_V3: 7691d9029d4SLorenzo Pieralisi return IS_BUILTIN(CONFIG_ARM_SMMU_V3); 7701d9029d4SLorenzo Pieralisi case ACPI_IORT_NODE_SMMU: 7711d9029d4SLorenzo Pieralisi return IS_BUILTIN(CONFIG_ARM_SMMU); 7721d9029d4SLorenzo Pieralisi default: 7731d9029d4SLorenzo Pieralisi pr_warn("IORT node type %u does not describe an SMMU\n", type); 7741d9029d4SLorenzo Pieralisi return false; 7751d9029d4SLorenzo Pieralisi } 7761d9029d4SLorenzo Pieralisi } 7771d9029d4SLorenzo Pieralisi 778d49f2dedSLorenzo Pieralisi #ifdef CONFIG_IOMMU_API 7798b4282e6SShameer Kolothum static struct acpi_iort_node *iort_get_msi_resv_iommu(struct device *dev) 7808b4282e6SShameer Kolothum { 7818b4282e6SShameer Kolothum struct acpi_iort_node *iommu; 7828097e53eSJoerg Roedel struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); 7838b4282e6SShameer Kolothum 7848b4282e6SShameer Kolothum iommu = iort_get_iort_node(fwspec->iommu_fwnode); 7858b4282e6SShameer Kolothum 7868b4282e6SShameer Kolothum if (iommu && (iommu->type == ACPI_IORT_NODE_SMMU_V3)) { 7878b4282e6SShameer Kolothum struct acpi_iort_smmu_v3 *smmu; 7888b4282e6SShameer Kolothum 7898b4282e6SShameer Kolothum smmu = (struct acpi_iort_smmu_v3 *)iommu->node_data; 7908b4282e6SShameer Kolothum if (smmu->model == ACPI_IORT_SMMU_V3_HISILICON_HI161X) 7918b4282e6SShameer Kolothum return iommu; 7928b4282e6SShameer Kolothum } 7938b4282e6SShameer Kolothum 7948b4282e6SShameer Kolothum return NULL; 7958b4282e6SShameer Kolothum } 7968b4282e6SShameer Kolothum 7978097e53eSJoerg Roedel static inline const struct iommu_ops *iort_fwspec_iommu_ops(struct device *dev) 798d49f2dedSLorenzo Pieralisi { 7998097e53eSJoerg Roedel struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); 8008097e53eSJoerg Roedel 801d49f2dedSLorenzo Pieralisi return (fwspec && fwspec->ops) ? fwspec->ops : NULL; 802d49f2dedSLorenzo Pieralisi } 803d49f2dedSLorenzo Pieralisi 804e3d49392SLorenzo Pieralisi static inline int iort_add_device_replay(const struct iommu_ops *ops, 805e3d49392SLorenzo Pieralisi struct device *dev) 806d49f2dedSLorenzo Pieralisi { 807d49f2dedSLorenzo Pieralisi int err = 0; 808d49f2dedSLorenzo Pieralisi 809d2e1a003SJoerg Roedel if (dev->bus && !device_iommu_mapped(dev)) 810d2e1a003SJoerg Roedel err = iommu_probe_device(dev); 811d49f2dedSLorenzo Pieralisi 812d49f2dedSLorenzo Pieralisi return err; 813d49f2dedSLorenzo Pieralisi } 8148b4282e6SShameer Kolothum 8158b4282e6SShameer Kolothum /** 8168b4282e6SShameer Kolothum * iort_iommu_msi_get_resv_regions - Reserved region driver helper 8178b4282e6SShameer Kolothum * @dev: Device from iommu_get_resv_regions() 8188b4282e6SShameer Kolothum * @head: Reserved region list from iommu_get_resv_regions() 8198b4282e6SShameer Kolothum * 8208b4282e6SShameer Kolothum * Returns: Number of msi reserved regions on success (0 if platform 8218b4282e6SShameer Kolothum * doesn't require the reservation or no associated msi regions), 8228b4282e6SShameer Kolothum * appropriate error value otherwise. The ITS interrupt translation 8238b4282e6SShameer Kolothum * spaces (ITS_base + SZ_64K, SZ_64K) associated with the device 8248b4282e6SShameer Kolothum * are the msi reserved regions. 8258b4282e6SShameer Kolothum */ 8268b4282e6SShameer Kolothum int iort_iommu_msi_get_resv_regions(struct device *dev, struct list_head *head) 8278b4282e6SShameer Kolothum { 8288097e53eSJoerg Roedel struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); 8298b4282e6SShameer Kolothum struct acpi_iort_its_group *its; 8308b4282e6SShameer Kolothum struct acpi_iort_node *iommu_node, *its_node = NULL; 8318b4282e6SShameer Kolothum int i, resv = 0; 8328b4282e6SShameer Kolothum 8338b4282e6SShameer Kolothum iommu_node = iort_get_msi_resv_iommu(dev); 8348b4282e6SShameer Kolothum if (!iommu_node) 8358b4282e6SShameer Kolothum return 0; 8368b4282e6SShameer Kolothum 8378b4282e6SShameer Kolothum /* 8388b4282e6SShameer Kolothum * Current logic to reserve ITS regions relies on HW topologies 8398b4282e6SShameer Kolothum * where a given PCI or named component maps its IDs to only one 8408b4282e6SShameer Kolothum * ITS group; if a PCI or named component can map its IDs to 8418b4282e6SShameer Kolothum * different ITS groups through IORT mappings this function has 8428b4282e6SShameer Kolothum * to be reworked to ensure we reserve regions for all ITS groups 8438b4282e6SShameer Kolothum * a given PCI or named component may map IDs to. 8448b4282e6SShameer Kolothum */ 8458b4282e6SShameer Kolothum 8468097e53eSJoerg Roedel for (i = 0; i < fwspec->num_ids; i++) { 8478b4282e6SShameer Kolothum its_node = iort_node_map_id(iommu_node, 8488097e53eSJoerg Roedel fwspec->ids[i], 8498b4282e6SShameer Kolothum NULL, IORT_MSI_TYPE); 8508b4282e6SShameer Kolothum if (its_node) 8518b4282e6SShameer Kolothum break; 8528b4282e6SShameer Kolothum } 8538b4282e6SShameer Kolothum 8548b4282e6SShameer Kolothum if (!its_node) 8558b4282e6SShameer Kolothum return 0; 8568b4282e6SShameer Kolothum 8578b4282e6SShameer Kolothum /* Move to ITS specific data */ 8588b4282e6SShameer Kolothum its = (struct acpi_iort_its_group *)its_node->node_data; 8598b4282e6SShameer Kolothum 8608b4282e6SShameer Kolothum for (i = 0; i < its->its_count; i++) { 8618b4282e6SShameer Kolothum phys_addr_t base; 8628b4282e6SShameer Kolothum 8638b4282e6SShameer Kolothum if (!iort_find_its_base(its->identifiers[i], &base)) { 8648b4282e6SShameer Kolothum int prot = IOMMU_WRITE | IOMMU_NOEXEC | IOMMU_MMIO; 8658b4282e6SShameer Kolothum struct iommu_resv_region *region; 8668b4282e6SShameer Kolothum 8678b4282e6SShameer Kolothum region = iommu_alloc_resv_region(base + SZ_64K, SZ_64K, 8688b4282e6SShameer Kolothum prot, IOMMU_RESV_MSI); 8698b4282e6SShameer Kolothum if (region) { 8708b4282e6SShameer Kolothum list_add_tail(®ion->list, head); 8718b4282e6SShameer Kolothum resv++; 8728b4282e6SShameer Kolothum } 8738b4282e6SShameer Kolothum } 8748b4282e6SShameer Kolothum } 8758b4282e6SShameer Kolothum 8768b4282e6SShameer Kolothum return (resv == its->its_count) ? resv : -ENODEV; 8778b4282e6SShameer Kolothum } 878d49f2dedSLorenzo Pieralisi #else 8796b68835bSQian Cai static inline const struct iommu_ops *iort_fwspec_iommu_ops(struct device *dev) 880d49f2dedSLorenzo Pieralisi { return NULL; } 881e3d49392SLorenzo Pieralisi static inline int iort_add_device_replay(const struct iommu_ops *ops, 882e3d49392SLorenzo Pieralisi struct device *dev) 883d49f2dedSLorenzo Pieralisi { return 0; } 8848b4282e6SShameer Kolothum int iort_iommu_msi_get_resv_regions(struct device *dev, struct list_head *head) 8858b4282e6SShameer Kolothum { return 0; } 886d49f2dedSLorenzo Pieralisi #endif 887d49f2dedSLorenzo Pieralisi 888bc8648d4SRobin Murphy static int iort_iommu_xlate(struct device *dev, struct acpi_iort_node *node, 889643b8e4dSLorenzo Pieralisi u32 streamid) 890643b8e4dSLorenzo Pieralisi { 891bc8648d4SRobin Murphy const struct iommu_ops *ops; 892643b8e4dSLorenzo Pieralisi struct fwnode_handle *iort_fwnode; 893643b8e4dSLorenzo Pieralisi 894bc8648d4SRobin Murphy if (!node) 895bc8648d4SRobin Murphy return -ENODEV; 896bc8648d4SRobin Murphy 897643b8e4dSLorenzo Pieralisi iort_fwnode = iort_get_fwnode(node); 898643b8e4dSLorenzo Pieralisi if (!iort_fwnode) 899bc8648d4SRobin Murphy return -ENODEV; 900643b8e4dSLorenzo Pieralisi 9015a1bb638SSricharan R /* 9025a1bb638SSricharan R * If the ops look-up fails, this means that either 9035a1bb638SSricharan R * the SMMU drivers have not been probed yet or that 9045a1bb638SSricharan R * the SMMU drivers are not built in the kernel; 9055a1bb638SSricharan R * Depending on whether the SMMU drivers are built-in 9065a1bb638SSricharan R * in the kernel or not, defer the IOMMU configuration 9075a1bb638SSricharan R * or just abort it. 9085a1bb638SSricharan R */ 909bc8648d4SRobin Murphy ops = iommu_ops_from_fwnode(iort_fwnode); 910643b8e4dSLorenzo Pieralisi if (!ops) 9115a1bb638SSricharan R return iort_iommu_driver_enabled(node->type) ? 912bc8648d4SRobin Murphy -EPROBE_DEFER : -ENODEV; 913643b8e4dSLorenzo Pieralisi 914bc8648d4SRobin Murphy return arm_smmu_iort_xlate(dev, streamid, iort_fwnode, ops); 915643b8e4dSLorenzo Pieralisi } 916643b8e4dSLorenzo Pieralisi 917bc8648d4SRobin Murphy struct iort_pci_alias_info { 918bc8648d4SRobin Murphy struct device *dev; 919bc8648d4SRobin Murphy struct acpi_iort_node *node; 920bc8648d4SRobin Murphy }; 921bc8648d4SRobin Murphy 922bc8648d4SRobin Murphy static int iort_pci_iommu_init(struct pci_dev *pdev, u16 alias, void *data) 923bc8648d4SRobin Murphy { 924bc8648d4SRobin Murphy struct iort_pci_alias_info *info = data; 925bc8648d4SRobin Murphy struct acpi_iort_node *parent; 926bc8648d4SRobin Murphy u32 streamid; 927bc8648d4SRobin Murphy 928bc8648d4SRobin Murphy parent = iort_node_map_id(info->node, alias, &streamid, 929bc8648d4SRobin Murphy IORT_IOMMU_TYPE); 930bc8648d4SRobin Murphy return iort_iommu_xlate(info->dev, parent, streamid); 931643b8e4dSLorenzo Pieralisi } 932643b8e4dSLorenzo Pieralisi 93310d8ab2cSLorenzo Pieralisi static int nc_dma_get_range(struct device *dev, u64 *size) 93410d8ab2cSLorenzo Pieralisi { 93510d8ab2cSLorenzo Pieralisi struct acpi_iort_node *node; 93610d8ab2cSLorenzo Pieralisi struct acpi_iort_named_component *ncomp; 93710d8ab2cSLorenzo Pieralisi 93810d8ab2cSLorenzo Pieralisi node = iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT, 93910d8ab2cSLorenzo Pieralisi iort_match_node_callback, dev); 94010d8ab2cSLorenzo Pieralisi if (!node) 94110d8ab2cSLorenzo Pieralisi return -ENODEV; 94210d8ab2cSLorenzo Pieralisi 94310d8ab2cSLorenzo Pieralisi ncomp = (struct acpi_iort_named_component *)node->node_data; 94410d8ab2cSLorenzo Pieralisi 94510d8ab2cSLorenzo Pieralisi *size = ncomp->memory_address_limit >= 64 ? U64_MAX : 94610d8ab2cSLorenzo Pieralisi 1ULL<<ncomp->memory_address_limit; 94710d8ab2cSLorenzo Pieralisi 94810d8ab2cSLorenzo Pieralisi return 0; 94910d8ab2cSLorenzo Pieralisi } 95010d8ab2cSLorenzo Pieralisi 9515ac65e8cSRobin Murphy static int rc_dma_get_range(struct device *dev, u64 *size) 9525ac65e8cSRobin Murphy { 9535ac65e8cSRobin Murphy struct acpi_iort_node *node; 9545ac65e8cSRobin Murphy struct acpi_iort_root_complex *rc; 955c7777236SJean-Philippe Brucker struct pci_bus *pbus = to_pci_dev(dev)->bus; 9565ac65e8cSRobin Murphy 9575ac65e8cSRobin Murphy node = iort_scan_node(ACPI_IORT_NODE_PCI_ROOT_COMPLEX, 958c7777236SJean-Philippe Brucker iort_match_node_callback, &pbus->dev); 9595ac65e8cSRobin Murphy if (!node || node->revision < 1) 9605ac65e8cSRobin Murphy return -ENODEV; 9615ac65e8cSRobin Murphy 9625ac65e8cSRobin Murphy rc = (struct acpi_iort_root_complex *)node->node_data; 9635ac65e8cSRobin Murphy 9645ac65e8cSRobin Murphy *size = rc->memory_address_limit >= 64 ? U64_MAX : 9655ac65e8cSRobin Murphy 1ULL<<rc->memory_address_limit; 9665ac65e8cSRobin Murphy 9675ac65e8cSRobin Murphy return 0; 9685ac65e8cSRobin Murphy } 9695ac65e8cSRobin Murphy 970643b8e4dSLorenzo Pieralisi /** 9717ad42639SLorenzo Pieralisi * iort_dma_setup() - Set-up device DMA parameters. 97218b709beSLorenzo Pieralisi * 97318b709beSLorenzo Pieralisi * @dev: device to configure 9747ad42639SLorenzo Pieralisi * @dma_addr: device DMA address result pointer 9757ad42639SLorenzo Pieralisi * @size: DMA range size result pointer 97618b709beSLorenzo Pieralisi */ 9777ad42639SLorenzo Pieralisi void iort_dma_setup(struct device *dev, u64 *dma_addr, u64 *dma_size) 97818b709beSLorenzo Pieralisi { 9797ad42639SLorenzo Pieralisi u64 mask, dmaaddr = 0, size = 0, offset = 0; 9807ad42639SLorenzo Pieralisi int ret, msb; 9817ad42639SLorenzo Pieralisi 98218b709beSLorenzo Pieralisi /* 9836757cdaeSRobin Murphy * If @dev is expected to be DMA-capable then the bus code that created 9846757cdaeSRobin Murphy * it should have initialised its dma_mask pointer by this point. For 9856757cdaeSRobin Murphy * now, we'll continue the legacy behaviour of coercing it to the 9866757cdaeSRobin Murphy * coherent mask if not, but we'll no longer do so quietly. 98718b709beSLorenzo Pieralisi */ 9886757cdaeSRobin Murphy if (!dev->dma_mask) { 9896757cdaeSRobin Murphy dev_warn(dev, "DMA mask not set\n"); 99018b709beSLorenzo Pieralisi dev->dma_mask = &dev->coherent_dma_mask; 9916757cdaeSRobin Murphy } 9927ad42639SLorenzo Pieralisi 9936757cdaeSRobin Murphy if (dev->coherent_dma_mask) 9947ad42639SLorenzo Pieralisi size = max(dev->coherent_dma_mask, dev->coherent_dma_mask + 1); 9956757cdaeSRobin Murphy else 9966757cdaeSRobin Murphy size = 1ULL << 32; 9977ad42639SLorenzo Pieralisi 9985ac65e8cSRobin Murphy if (dev_is_pci(dev)) { 9997ad42639SLorenzo Pieralisi ret = acpi_dma_get_range(dev, &dmaaddr, &offset, &size); 10005ac65e8cSRobin Murphy if (ret == -ENODEV) 10015ac65e8cSRobin Murphy ret = rc_dma_get_range(dev, &size); 10025ac65e8cSRobin Murphy } else { 100310d8ab2cSLorenzo Pieralisi ret = nc_dma_get_range(dev, &size); 10045ac65e8cSRobin Murphy } 100510d8ab2cSLorenzo Pieralisi 10067ad42639SLorenzo Pieralisi if (!ret) { 10077ad42639SLorenzo Pieralisi msb = fls64(dmaaddr + size - 1); 10087ad42639SLorenzo Pieralisi /* 10097ad42639SLorenzo Pieralisi * Round-up to the power-of-two mask or set 10107ad42639SLorenzo Pieralisi * the mask to the whole 64-bit address space 10117ad42639SLorenzo Pieralisi * in case the DMA region covers the full 10127ad42639SLorenzo Pieralisi * memory window. 10137ad42639SLorenzo Pieralisi */ 10147ad42639SLorenzo Pieralisi mask = msb == 64 ? U64_MAX : (1ULL << msb) - 1; 10157ad42639SLorenzo Pieralisi /* 10167ad42639SLorenzo Pieralisi * Limit coherent and dma mask based on size 10177ad42639SLorenzo Pieralisi * retrieved from firmware. 10187ad42639SLorenzo Pieralisi */ 1019d74ea710SRobin Murphy dev->bus_dma_mask = mask; 10207ad42639SLorenzo Pieralisi dev->coherent_dma_mask = mask; 10217ad42639SLorenzo Pieralisi *dev->dma_mask = mask; 10227ad42639SLorenzo Pieralisi } 10237ad42639SLorenzo Pieralisi 10247ad42639SLorenzo Pieralisi *dma_addr = dmaaddr; 10257ad42639SLorenzo Pieralisi *dma_size = size; 10267ad42639SLorenzo Pieralisi 10277ad42639SLorenzo Pieralisi dev->dma_pfn_offset = PFN_DOWN(offset); 10287ad42639SLorenzo Pieralisi dev_dbg(dev, "dma_pfn_offset(%#08llx)\n", offset); 102918b709beSLorenzo Pieralisi } 103018b709beSLorenzo Pieralisi 103118b709beSLorenzo Pieralisi /** 1032643b8e4dSLorenzo Pieralisi * iort_iommu_configure - Set-up IOMMU configuration for a device. 1033643b8e4dSLorenzo Pieralisi * 1034643b8e4dSLorenzo Pieralisi * @dev: device to configure 1035643b8e4dSLorenzo Pieralisi * 1036643b8e4dSLorenzo Pieralisi * Returns: iommu_ops pointer on configuration success 1037643b8e4dSLorenzo Pieralisi * NULL on configuration failure 1038643b8e4dSLorenzo Pieralisi */ 1039643b8e4dSLorenzo Pieralisi const struct iommu_ops *iort_iommu_configure(struct device *dev) 1040643b8e4dSLorenzo Pieralisi { 1041643b8e4dSLorenzo Pieralisi struct acpi_iort_node *node, *parent; 1042bc8648d4SRobin Murphy const struct iommu_ops *ops; 1043643b8e4dSLorenzo Pieralisi u32 streamid = 0; 1044bc8648d4SRobin Murphy int err = -ENODEV; 1045643b8e4dSLorenzo Pieralisi 10464dac3210SLorenzo Pieralisi /* 10474dac3210SLorenzo Pieralisi * If we already translated the fwspec there 10484dac3210SLorenzo Pieralisi * is nothing left to do, return the iommu_ops. 10494dac3210SLorenzo Pieralisi */ 10508097e53eSJoerg Roedel ops = iort_fwspec_iommu_ops(dev); 10514dac3210SLorenzo Pieralisi if (ops) 10524dac3210SLorenzo Pieralisi return ops; 10534dac3210SLorenzo Pieralisi 1054643b8e4dSLorenzo Pieralisi if (dev_is_pci(dev)) { 1055643b8e4dSLorenzo Pieralisi struct pci_bus *bus = to_pci_dev(dev)->bus; 1056bc8648d4SRobin Murphy struct iort_pci_alias_info info = { .dev = dev }; 1057643b8e4dSLorenzo Pieralisi 1058643b8e4dSLorenzo Pieralisi node = iort_scan_node(ACPI_IORT_NODE_PCI_ROOT_COMPLEX, 1059643b8e4dSLorenzo Pieralisi iort_match_node_callback, &bus->dev); 1060643b8e4dSLorenzo Pieralisi if (!node) 1061643b8e4dSLorenzo Pieralisi return NULL; 1062643b8e4dSLorenzo Pieralisi 1063bc8648d4SRobin Murphy info.node = node; 1064bc8648d4SRobin Murphy err = pci_for_each_dma_alias(to_pci_dev(dev), 1065bc8648d4SRobin Murphy iort_pci_iommu_init, &info); 1066643b8e4dSLorenzo Pieralisi } else { 1067643b8e4dSLorenzo Pieralisi int i = 0; 1068643b8e4dSLorenzo Pieralisi 1069643b8e4dSLorenzo Pieralisi node = iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT, 1070643b8e4dSLorenzo Pieralisi iort_match_node_callback, dev); 1071643b8e4dSLorenzo Pieralisi if (!node) 1072643b8e4dSLorenzo Pieralisi return NULL; 1073643b8e4dSLorenzo Pieralisi 1074bc8648d4SRobin Murphy do { 10758ca4f1d3SHanjun Guo parent = iort_node_map_platform_id(node, &streamid, 10768ca4f1d3SHanjun Guo IORT_IOMMU_TYPE, 10778ca4f1d3SHanjun Guo i++); 1078bc8648d4SRobin Murphy 1079bc8648d4SRobin Murphy if (parent) 1080bc8648d4SRobin Murphy err = iort_iommu_xlate(dev, parent, streamid); 1081bc8648d4SRobin Murphy } while (parent && !err); 1082643b8e4dSLorenzo Pieralisi } 1083643b8e4dSLorenzo Pieralisi 10845a1bb638SSricharan R /* 10855a1bb638SSricharan R * If we have reason to believe the IOMMU driver missed the initial 10865a1bb638SSricharan R * add_device callback for dev, replay it to get things in order. 10875a1bb638SSricharan R */ 1088bc8648d4SRobin Murphy if (!err) { 10898097e53eSJoerg Roedel ops = iort_fwspec_iommu_ops(dev); 1090d49f2dedSLorenzo Pieralisi err = iort_add_device_replay(ops, dev); 1091bc8648d4SRobin Murphy } 10925a1bb638SSricharan R 1093058f8c3fSSricharan R /* Ignore all other errors apart from EPROBE_DEFER */ 1094bc8648d4SRobin Murphy if (err == -EPROBE_DEFER) { 1095bc8648d4SRobin Murphy ops = ERR_PTR(err); 1096bc8648d4SRobin Murphy } else if (err) { 1097bc8648d4SRobin Murphy dev_dbg(dev, "Adding to IOMMU failed: %d\n", err); 1098058f8c3fSSricharan R ops = NULL; 1099058f8c3fSSricharan R } 1100058f8c3fSSricharan R 1101643b8e4dSLorenzo Pieralisi return ops; 1102643b8e4dSLorenzo Pieralisi } 1103643b8e4dSLorenzo Pieralisi 1104e4dadfa8SLorenzo Pieralisi static void __init acpi_iort_register_irq(int hwirq, const char *name, 1105e4dadfa8SLorenzo Pieralisi int trigger, 1106e4dadfa8SLorenzo Pieralisi struct resource *res) 1107e4dadfa8SLorenzo Pieralisi { 1108e4dadfa8SLorenzo Pieralisi int irq = acpi_register_gsi(NULL, hwirq, trigger, 1109e4dadfa8SLorenzo Pieralisi ACPI_ACTIVE_HIGH); 1110e4dadfa8SLorenzo Pieralisi 1111e4dadfa8SLorenzo Pieralisi if (irq <= 0) { 1112e4dadfa8SLorenzo Pieralisi pr_err("could not register gsi hwirq %d name [%s]\n", hwirq, 1113e4dadfa8SLorenzo Pieralisi name); 1114e4dadfa8SLorenzo Pieralisi return; 1115e4dadfa8SLorenzo Pieralisi } 1116e4dadfa8SLorenzo Pieralisi 1117e4dadfa8SLorenzo Pieralisi res->start = irq; 1118e4dadfa8SLorenzo Pieralisi res->end = irq; 1119e4dadfa8SLorenzo Pieralisi res->flags = IORESOURCE_IRQ; 1120e4dadfa8SLorenzo Pieralisi res->name = name; 1121e4dadfa8SLorenzo Pieralisi } 1122e4dadfa8SLorenzo Pieralisi 1123e4dadfa8SLorenzo Pieralisi static int __init arm_smmu_v3_count_resources(struct acpi_iort_node *node) 1124e4dadfa8SLorenzo Pieralisi { 1125e4dadfa8SLorenzo Pieralisi struct acpi_iort_smmu_v3 *smmu; 1126e4dadfa8SLorenzo Pieralisi /* Always present mem resource */ 1127e4dadfa8SLorenzo Pieralisi int num_res = 1; 1128e4dadfa8SLorenzo Pieralisi 1129e4dadfa8SLorenzo Pieralisi /* Retrieve SMMUv3 specific data */ 1130e4dadfa8SLorenzo Pieralisi smmu = (struct acpi_iort_smmu_v3 *)node->node_data; 1131e4dadfa8SLorenzo Pieralisi 1132e4dadfa8SLorenzo Pieralisi if (smmu->event_gsiv) 1133e4dadfa8SLorenzo Pieralisi num_res++; 1134e4dadfa8SLorenzo Pieralisi 1135e4dadfa8SLorenzo Pieralisi if (smmu->pri_gsiv) 1136e4dadfa8SLorenzo Pieralisi num_res++; 1137e4dadfa8SLorenzo Pieralisi 1138e4dadfa8SLorenzo Pieralisi if (smmu->gerr_gsiv) 1139e4dadfa8SLorenzo Pieralisi num_res++; 1140e4dadfa8SLorenzo Pieralisi 1141e4dadfa8SLorenzo Pieralisi if (smmu->sync_gsiv) 1142e4dadfa8SLorenzo Pieralisi num_res++; 1143e4dadfa8SLorenzo Pieralisi 1144e4dadfa8SLorenzo Pieralisi return num_res; 1145e4dadfa8SLorenzo Pieralisi } 1146e4dadfa8SLorenzo Pieralisi 1147f935448aSGeetha Sowjanya static bool arm_smmu_v3_is_combined_irq(struct acpi_iort_smmu_v3 *smmu) 1148f935448aSGeetha Sowjanya { 1149f935448aSGeetha Sowjanya /* 1150f935448aSGeetha Sowjanya * Cavium ThunderX2 implementation doesn't not support unique 1151f935448aSGeetha Sowjanya * irq line. Use single irq line for all the SMMUv3 interrupts. 1152f935448aSGeetha Sowjanya */ 1153f935448aSGeetha Sowjanya if (smmu->model != ACPI_IORT_SMMU_V3_CAVIUM_CN99XX) 1154f935448aSGeetha Sowjanya return false; 1155f935448aSGeetha Sowjanya 1156f935448aSGeetha Sowjanya /* 1157f935448aSGeetha Sowjanya * ThunderX2 doesn't support MSIs from the SMMU, so we're checking 1158f935448aSGeetha Sowjanya * SPI numbers here. 1159f935448aSGeetha Sowjanya */ 1160f935448aSGeetha Sowjanya return smmu->event_gsiv == smmu->pri_gsiv && 1161f935448aSGeetha Sowjanya smmu->event_gsiv == smmu->gerr_gsiv && 1162f935448aSGeetha Sowjanya smmu->event_gsiv == smmu->sync_gsiv; 1163f935448aSGeetha Sowjanya } 1164f935448aSGeetha Sowjanya 1165403e8c7cSLinu Cherian static unsigned long arm_smmu_v3_resource_size(struct acpi_iort_smmu_v3 *smmu) 1166403e8c7cSLinu Cherian { 1167403e8c7cSLinu Cherian /* 1168403e8c7cSLinu Cherian * Override the size, for Cavium ThunderX2 implementation 1169403e8c7cSLinu Cherian * which doesn't support the page 1 SMMU register space. 1170403e8c7cSLinu Cherian */ 1171403e8c7cSLinu Cherian if (smmu->model == ACPI_IORT_SMMU_V3_CAVIUM_CN99XX) 1172403e8c7cSLinu Cherian return SZ_64K; 1173403e8c7cSLinu Cherian 1174403e8c7cSLinu Cherian return SZ_128K; 1175403e8c7cSLinu Cherian } 1176403e8c7cSLinu Cherian 1177e4dadfa8SLorenzo Pieralisi static void __init arm_smmu_v3_init_resources(struct resource *res, 1178e4dadfa8SLorenzo Pieralisi struct acpi_iort_node *node) 1179e4dadfa8SLorenzo Pieralisi { 1180e4dadfa8SLorenzo Pieralisi struct acpi_iort_smmu_v3 *smmu; 1181e4dadfa8SLorenzo Pieralisi int num_res = 0; 1182e4dadfa8SLorenzo Pieralisi 1183e4dadfa8SLorenzo Pieralisi /* Retrieve SMMUv3 specific data */ 1184e4dadfa8SLorenzo Pieralisi smmu = (struct acpi_iort_smmu_v3 *)node->node_data; 1185e4dadfa8SLorenzo Pieralisi 1186e4dadfa8SLorenzo Pieralisi res[num_res].start = smmu->base_address; 1187403e8c7cSLinu Cherian res[num_res].end = smmu->base_address + 1188403e8c7cSLinu Cherian arm_smmu_v3_resource_size(smmu) - 1; 1189e4dadfa8SLorenzo Pieralisi res[num_res].flags = IORESOURCE_MEM; 1190e4dadfa8SLorenzo Pieralisi 1191e4dadfa8SLorenzo Pieralisi num_res++; 1192f935448aSGeetha Sowjanya if (arm_smmu_v3_is_combined_irq(smmu)) { 1193f935448aSGeetha Sowjanya if (smmu->event_gsiv) 1194f935448aSGeetha Sowjanya acpi_iort_register_irq(smmu->event_gsiv, "combined", 1195f935448aSGeetha Sowjanya ACPI_EDGE_SENSITIVE, 1196f935448aSGeetha Sowjanya &res[num_res++]); 1197f935448aSGeetha Sowjanya } else { 1198e4dadfa8SLorenzo Pieralisi 1199e4dadfa8SLorenzo Pieralisi if (smmu->event_gsiv) 1200e4dadfa8SLorenzo Pieralisi acpi_iort_register_irq(smmu->event_gsiv, "eventq", 1201e4dadfa8SLorenzo Pieralisi ACPI_EDGE_SENSITIVE, 1202e4dadfa8SLorenzo Pieralisi &res[num_res++]); 1203e4dadfa8SLorenzo Pieralisi 1204e4dadfa8SLorenzo Pieralisi if (smmu->pri_gsiv) 1205e4dadfa8SLorenzo Pieralisi acpi_iort_register_irq(smmu->pri_gsiv, "priq", 1206e4dadfa8SLorenzo Pieralisi ACPI_EDGE_SENSITIVE, 1207e4dadfa8SLorenzo Pieralisi &res[num_res++]); 1208e4dadfa8SLorenzo Pieralisi 1209e4dadfa8SLorenzo Pieralisi if (smmu->gerr_gsiv) 1210e4dadfa8SLorenzo Pieralisi acpi_iort_register_irq(smmu->gerr_gsiv, "gerror", 1211e4dadfa8SLorenzo Pieralisi ACPI_EDGE_SENSITIVE, 1212e4dadfa8SLorenzo Pieralisi &res[num_res++]); 1213e4dadfa8SLorenzo Pieralisi 1214e4dadfa8SLorenzo Pieralisi if (smmu->sync_gsiv) 1215e4dadfa8SLorenzo Pieralisi acpi_iort_register_irq(smmu->sync_gsiv, "cmdq-sync", 1216e4dadfa8SLorenzo Pieralisi ACPI_EDGE_SENSITIVE, 1217e4dadfa8SLorenzo Pieralisi &res[num_res++]); 1218e4dadfa8SLorenzo Pieralisi } 1219f935448aSGeetha Sowjanya } 1220e4dadfa8SLorenzo Pieralisi 1221e4dadfa8SLorenzo Pieralisi static bool __init arm_smmu_v3_is_coherent(struct acpi_iort_node *node) 1222e4dadfa8SLorenzo Pieralisi { 1223e4dadfa8SLorenzo Pieralisi struct acpi_iort_smmu_v3 *smmu; 1224e4dadfa8SLorenzo Pieralisi 1225e4dadfa8SLorenzo Pieralisi /* Retrieve SMMUv3 specific data */ 1226e4dadfa8SLorenzo Pieralisi smmu = (struct acpi_iort_smmu_v3 *)node->node_data; 1227e4dadfa8SLorenzo Pieralisi 1228e4dadfa8SLorenzo Pieralisi return smmu->flags & ACPI_IORT_SMMU_V3_COHACC_OVERRIDE; 1229e4dadfa8SLorenzo Pieralisi } 1230e4dadfa8SLorenzo Pieralisi 123175808131SLorenzo Pieralisi #if defined(CONFIG_ACPI_NUMA) 12325fe0ce3bSGanapatrao Kulkarni /* 12335fe0ce3bSGanapatrao Kulkarni * set numa proximity domain for smmuv3 device 12345fe0ce3bSGanapatrao Kulkarni */ 1235*36a2ba07SKefeng Wang static int __init arm_smmu_v3_set_proximity(struct device *dev, 12365fe0ce3bSGanapatrao Kulkarni struct acpi_iort_node *node) 12375fe0ce3bSGanapatrao Kulkarni { 12385fe0ce3bSGanapatrao Kulkarni struct acpi_iort_smmu_v3 *smmu; 12395fe0ce3bSGanapatrao Kulkarni 12405fe0ce3bSGanapatrao Kulkarni smmu = (struct acpi_iort_smmu_v3 *)node->node_data; 12415fe0ce3bSGanapatrao Kulkarni if (smmu->flags & ACPI_IORT_SMMU_V3_PXM_VALID) { 1242*36a2ba07SKefeng Wang int node = acpi_map_pxm_to_node(smmu->pxm); 1243*36a2ba07SKefeng Wang 1244*36a2ba07SKefeng Wang if (node != NUMA_NO_NODE && !node_online(node)) 1245*36a2ba07SKefeng Wang return -EINVAL; 1246*36a2ba07SKefeng Wang 1247*36a2ba07SKefeng Wang set_dev_node(dev, node); 12485fe0ce3bSGanapatrao Kulkarni pr_info("SMMU-v3[%llx] Mapped to Proximity domain %d\n", 12495fe0ce3bSGanapatrao Kulkarni smmu->base_address, 12505fe0ce3bSGanapatrao Kulkarni smmu->pxm); 12515fe0ce3bSGanapatrao Kulkarni } 1252*36a2ba07SKefeng Wang return 0; 12535fe0ce3bSGanapatrao Kulkarni } 12545fe0ce3bSGanapatrao Kulkarni #else 12555fe0ce3bSGanapatrao Kulkarni #define arm_smmu_v3_set_proximity NULL 12565fe0ce3bSGanapatrao Kulkarni #endif 12575fe0ce3bSGanapatrao Kulkarni 1258d6fcd3b1SLorenzo Pieralisi static int __init arm_smmu_count_resources(struct acpi_iort_node *node) 1259d6fcd3b1SLorenzo Pieralisi { 1260d6fcd3b1SLorenzo Pieralisi struct acpi_iort_smmu *smmu; 1261d6fcd3b1SLorenzo Pieralisi 1262d6fcd3b1SLorenzo Pieralisi /* Retrieve SMMU specific data */ 1263d6fcd3b1SLorenzo Pieralisi smmu = (struct acpi_iort_smmu *)node->node_data; 1264d6fcd3b1SLorenzo Pieralisi 1265d6fcd3b1SLorenzo Pieralisi /* 1266d6fcd3b1SLorenzo Pieralisi * Only consider the global fault interrupt and ignore the 1267d6fcd3b1SLorenzo Pieralisi * configuration access interrupt. 1268d6fcd3b1SLorenzo Pieralisi * 1269d6fcd3b1SLorenzo Pieralisi * MMIO address and global fault interrupt resources are always 1270d6fcd3b1SLorenzo Pieralisi * present so add them to the context interrupt count as a static 1271d6fcd3b1SLorenzo Pieralisi * value. 1272d6fcd3b1SLorenzo Pieralisi */ 1273d6fcd3b1SLorenzo Pieralisi return smmu->context_interrupt_count + 2; 1274d6fcd3b1SLorenzo Pieralisi } 1275d6fcd3b1SLorenzo Pieralisi 1276d6fcd3b1SLorenzo Pieralisi static void __init arm_smmu_init_resources(struct resource *res, 1277d6fcd3b1SLorenzo Pieralisi struct acpi_iort_node *node) 1278d6fcd3b1SLorenzo Pieralisi { 1279d6fcd3b1SLorenzo Pieralisi struct acpi_iort_smmu *smmu; 1280d6fcd3b1SLorenzo Pieralisi int i, hw_irq, trigger, num_res = 0; 1281d6fcd3b1SLorenzo Pieralisi u64 *ctx_irq, *glb_irq; 1282d6fcd3b1SLorenzo Pieralisi 1283d6fcd3b1SLorenzo Pieralisi /* Retrieve SMMU specific data */ 1284d6fcd3b1SLorenzo Pieralisi smmu = (struct acpi_iort_smmu *)node->node_data; 1285d6fcd3b1SLorenzo Pieralisi 1286d6fcd3b1SLorenzo Pieralisi res[num_res].start = smmu->base_address; 1287d6fcd3b1SLorenzo Pieralisi res[num_res].end = smmu->base_address + smmu->span - 1; 1288d6fcd3b1SLorenzo Pieralisi res[num_res].flags = IORESOURCE_MEM; 1289d6fcd3b1SLorenzo Pieralisi num_res++; 1290d6fcd3b1SLorenzo Pieralisi 1291d6fcd3b1SLorenzo Pieralisi glb_irq = ACPI_ADD_PTR(u64, node, smmu->global_interrupt_offset); 1292d6fcd3b1SLorenzo Pieralisi /* Global IRQs */ 1293d6fcd3b1SLorenzo Pieralisi hw_irq = IORT_IRQ_MASK(glb_irq[0]); 1294d6fcd3b1SLorenzo Pieralisi trigger = IORT_IRQ_TRIGGER_MASK(glb_irq[0]); 1295d6fcd3b1SLorenzo Pieralisi 1296d6fcd3b1SLorenzo Pieralisi acpi_iort_register_irq(hw_irq, "arm-smmu-global", trigger, 1297d6fcd3b1SLorenzo Pieralisi &res[num_res++]); 1298d6fcd3b1SLorenzo Pieralisi 1299d6fcd3b1SLorenzo Pieralisi /* Context IRQs */ 1300d6fcd3b1SLorenzo Pieralisi ctx_irq = ACPI_ADD_PTR(u64, node, smmu->context_interrupt_offset); 1301d6fcd3b1SLorenzo Pieralisi for (i = 0; i < smmu->context_interrupt_count; i++) { 1302d6fcd3b1SLorenzo Pieralisi hw_irq = IORT_IRQ_MASK(ctx_irq[i]); 1303d6fcd3b1SLorenzo Pieralisi trigger = IORT_IRQ_TRIGGER_MASK(ctx_irq[i]); 1304d6fcd3b1SLorenzo Pieralisi 1305d6fcd3b1SLorenzo Pieralisi acpi_iort_register_irq(hw_irq, "arm-smmu-context", trigger, 1306d6fcd3b1SLorenzo Pieralisi &res[num_res++]); 1307d6fcd3b1SLorenzo Pieralisi } 1308d6fcd3b1SLorenzo Pieralisi } 1309d6fcd3b1SLorenzo Pieralisi 1310d6fcd3b1SLorenzo Pieralisi static bool __init arm_smmu_is_coherent(struct acpi_iort_node *node) 1311d6fcd3b1SLorenzo Pieralisi { 1312d6fcd3b1SLorenzo Pieralisi struct acpi_iort_smmu *smmu; 1313d6fcd3b1SLorenzo Pieralisi 1314d6fcd3b1SLorenzo Pieralisi /* Retrieve SMMU specific data */ 1315d6fcd3b1SLorenzo Pieralisi smmu = (struct acpi_iort_smmu *)node->node_data; 1316d6fcd3b1SLorenzo Pieralisi 1317d6fcd3b1SLorenzo Pieralisi return smmu->flags & ACPI_IORT_SMMU_COHERENT_WALK; 1318d6fcd3b1SLorenzo Pieralisi } 1319d6fcd3b1SLorenzo Pieralisi 1320896dd2c3SLorenzo Pieralisi struct iort_dev_config { 1321846f0e9eSLorenzo Pieralisi const char *name; 1322896dd2c3SLorenzo Pieralisi int (*dev_init)(struct acpi_iort_node *node); 1323896dd2c3SLorenzo Pieralisi bool (*dev_is_coherent)(struct acpi_iort_node *node); 1324896dd2c3SLorenzo Pieralisi int (*dev_count_resources)(struct acpi_iort_node *node); 1325896dd2c3SLorenzo Pieralisi void (*dev_init_resources)(struct resource *res, 1326846f0e9eSLorenzo Pieralisi struct acpi_iort_node *node); 1327*36a2ba07SKefeng Wang int (*dev_set_proximity)(struct device *dev, 13285fe0ce3bSGanapatrao Kulkarni struct acpi_iort_node *node); 1329846f0e9eSLorenzo Pieralisi }; 1330846f0e9eSLorenzo Pieralisi 1331896dd2c3SLorenzo Pieralisi static const struct iort_dev_config iort_arm_smmu_v3_cfg __initconst = { 1332e4dadfa8SLorenzo Pieralisi .name = "arm-smmu-v3", 1333896dd2c3SLorenzo Pieralisi .dev_is_coherent = arm_smmu_v3_is_coherent, 1334896dd2c3SLorenzo Pieralisi .dev_count_resources = arm_smmu_v3_count_resources, 1335896dd2c3SLorenzo Pieralisi .dev_init_resources = arm_smmu_v3_init_resources, 1336896dd2c3SLorenzo Pieralisi .dev_set_proximity = arm_smmu_v3_set_proximity, 1337e4dadfa8SLorenzo Pieralisi }; 1338e4dadfa8SLorenzo Pieralisi 1339896dd2c3SLorenzo Pieralisi static const struct iort_dev_config iort_arm_smmu_cfg __initconst = { 1340d6fcd3b1SLorenzo Pieralisi .name = "arm-smmu", 1341896dd2c3SLorenzo Pieralisi .dev_is_coherent = arm_smmu_is_coherent, 1342896dd2c3SLorenzo Pieralisi .dev_count_resources = arm_smmu_count_resources, 1343896dd2c3SLorenzo Pieralisi .dev_init_resources = arm_smmu_init_resources 1344d6fcd3b1SLorenzo Pieralisi }; 1345d6fcd3b1SLorenzo Pieralisi 1346896dd2c3SLorenzo Pieralisi static __init const struct iort_dev_config *iort_get_dev_cfg( 1347e3d49392SLorenzo Pieralisi struct acpi_iort_node *node) 1348846f0e9eSLorenzo Pieralisi { 1349e4dadfa8SLorenzo Pieralisi switch (node->type) { 1350e4dadfa8SLorenzo Pieralisi case ACPI_IORT_NODE_SMMU_V3: 1351e4dadfa8SLorenzo Pieralisi return &iort_arm_smmu_v3_cfg; 1352d6fcd3b1SLorenzo Pieralisi case ACPI_IORT_NODE_SMMU: 1353d6fcd3b1SLorenzo Pieralisi return &iort_arm_smmu_cfg; 1354e4dadfa8SLorenzo Pieralisi default: 1355846f0e9eSLorenzo Pieralisi return NULL; 1356846f0e9eSLorenzo Pieralisi } 1357e4dadfa8SLorenzo Pieralisi } 1358846f0e9eSLorenzo Pieralisi 1359846f0e9eSLorenzo Pieralisi /** 1360896dd2c3SLorenzo Pieralisi * iort_add_platform_device() - Allocate a platform device for IORT node 1361896dd2c3SLorenzo Pieralisi * @node: Pointer to device ACPI IORT node 1362846f0e9eSLorenzo Pieralisi * 1363846f0e9eSLorenzo Pieralisi * Returns: 0 on success, <0 failure 1364846f0e9eSLorenzo Pieralisi */ 1365896dd2c3SLorenzo Pieralisi static int __init iort_add_platform_device(struct acpi_iort_node *node, 1366896dd2c3SLorenzo Pieralisi const struct iort_dev_config *ops) 1367846f0e9eSLorenzo Pieralisi { 1368846f0e9eSLorenzo Pieralisi struct fwnode_handle *fwnode; 1369846f0e9eSLorenzo Pieralisi struct platform_device *pdev; 1370846f0e9eSLorenzo Pieralisi struct resource *r; 1371846f0e9eSLorenzo Pieralisi enum dev_dma_attr attr; 1372846f0e9eSLorenzo Pieralisi int ret, count; 1373846f0e9eSLorenzo Pieralisi 1374846f0e9eSLorenzo Pieralisi pdev = platform_device_alloc(ops->name, PLATFORM_DEVID_AUTO); 1375846f0e9eSLorenzo Pieralisi if (!pdev) 13765e5afa6cSDan Carpenter return -ENOMEM; 1377846f0e9eSLorenzo Pieralisi 1378*36a2ba07SKefeng Wang if (ops->dev_set_proximity) { 1379*36a2ba07SKefeng Wang ret = ops->dev_set_proximity(&pdev->dev, node); 1380*36a2ba07SKefeng Wang if (ret) 1381*36a2ba07SKefeng Wang goto dev_put; 1382*36a2ba07SKefeng Wang } 13835fe0ce3bSGanapatrao Kulkarni 1384896dd2c3SLorenzo Pieralisi count = ops->dev_count_resources(node); 1385846f0e9eSLorenzo Pieralisi 1386846f0e9eSLorenzo Pieralisi r = kcalloc(count, sizeof(*r), GFP_KERNEL); 1387846f0e9eSLorenzo Pieralisi if (!r) { 1388846f0e9eSLorenzo Pieralisi ret = -ENOMEM; 1389846f0e9eSLorenzo Pieralisi goto dev_put; 1390846f0e9eSLorenzo Pieralisi } 1391846f0e9eSLorenzo Pieralisi 1392896dd2c3SLorenzo Pieralisi ops->dev_init_resources(r, node); 1393846f0e9eSLorenzo Pieralisi 1394846f0e9eSLorenzo Pieralisi ret = platform_device_add_resources(pdev, r, count); 1395846f0e9eSLorenzo Pieralisi /* 1396846f0e9eSLorenzo Pieralisi * Resources are duplicated in platform_device_add_resources, 1397846f0e9eSLorenzo Pieralisi * free their allocated memory 1398846f0e9eSLorenzo Pieralisi */ 1399846f0e9eSLorenzo Pieralisi kfree(r); 1400846f0e9eSLorenzo Pieralisi 1401846f0e9eSLorenzo Pieralisi if (ret) 1402846f0e9eSLorenzo Pieralisi goto dev_put; 1403846f0e9eSLorenzo Pieralisi 1404846f0e9eSLorenzo Pieralisi /* 1405846f0e9eSLorenzo Pieralisi * Add a copy of IORT node pointer to platform_data to 1406846f0e9eSLorenzo Pieralisi * be used to retrieve IORT data information. 1407846f0e9eSLorenzo Pieralisi */ 1408846f0e9eSLorenzo Pieralisi ret = platform_device_add_data(pdev, &node, sizeof(node)); 1409846f0e9eSLorenzo Pieralisi if (ret) 1410846f0e9eSLorenzo Pieralisi goto dev_put; 1411846f0e9eSLorenzo Pieralisi 1412846f0e9eSLorenzo Pieralisi /* 1413846f0e9eSLorenzo Pieralisi * We expect the dma masks to be equivalent for 1414846f0e9eSLorenzo Pieralisi * all SMMUs set-ups 1415846f0e9eSLorenzo Pieralisi */ 1416846f0e9eSLorenzo Pieralisi pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask; 1417846f0e9eSLorenzo Pieralisi 1418846f0e9eSLorenzo Pieralisi fwnode = iort_get_fwnode(node); 1419846f0e9eSLorenzo Pieralisi 1420846f0e9eSLorenzo Pieralisi if (!fwnode) { 1421846f0e9eSLorenzo Pieralisi ret = -ENODEV; 1422846f0e9eSLorenzo Pieralisi goto dev_put; 1423846f0e9eSLorenzo Pieralisi } 1424846f0e9eSLorenzo Pieralisi 1425846f0e9eSLorenzo Pieralisi pdev->dev.fwnode = fwnode; 1426846f0e9eSLorenzo Pieralisi 1427896dd2c3SLorenzo Pieralisi attr = ops->dev_is_coherent && ops->dev_is_coherent(node) ? 1428846f0e9eSLorenzo Pieralisi DEV_DMA_COHERENT : DEV_DMA_NON_COHERENT; 1429846f0e9eSLorenzo Pieralisi 1430846f0e9eSLorenzo Pieralisi /* Configure DMA for the page table walker */ 1431846f0e9eSLorenzo Pieralisi acpi_dma_configure(&pdev->dev, attr); 1432846f0e9eSLorenzo Pieralisi 143365637901SLorenzo Pieralisi iort_set_device_domain(&pdev->dev, node); 143465637901SLorenzo Pieralisi 1435846f0e9eSLorenzo Pieralisi ret = platform_device_add(pdev); 1436846f0e9eSLorenzo Pieralisi if (ret) 1437846f0e9eSLorenzo Pieralisi goto dma_deconfigure; 1438846f0e9eSLorenzo Pieralisi 1439846f0e9eSLorenzo Pieralisi return 0; 1440846f0e9eSLorenzo Pieralisi 1441846f0e9eSLorenzo Pieralisi dma_deconfigure: 1442dc3c0550SChristoph Hellwig arch_teardown_dma_ops(&pdev->dev); 1443846f0e9eSLorenzo Pieralisi dev_put: 1444846f0e9eSLorenzo Pieralisi platform_device_put(pdev); 1445846f0e9eSLorenzo Pieralisi 1446846f0e9eSLorenzo Pieralisi return ret; 1447846f0e9eSLorenzo Pieralisi } 1448846f0e9eSLorenzo Pieralisi 144943554cebSSinan Kaya #ifdef CONFIG_PCI 145043554cebSSinan Kaya static void __init iort_enable_acs(struct acpi_iort_node *iort_node) 145137f6b42eSLorenzo Pieralisi { 145243554cebSSinan Kaya static bool acs_enabled __initdata; 145343554cebSSinan Kaya 145443554cebSSinan Kaya if (acs_enabled) 145543554cebSSinan Kaya return; 145643554cebSSinan Kaya 145737f6b42eSLorenzo Pieralisi if (iort_node->type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX) { 145837f6b42eSLorenzo Pieralisi struct acpi_iort_node *parent; 145937f6b42eSLorenzo Pieralisi struct acpi_iort_id_mapping *map; 146037f6b42eSLorenzo Pieralisi int i; 146137f6b42eSLorenzo Pieralisi 146237f6b42eSLorenzo Pieralisi map = ACPI_ADD_PTR(struct acpi_iort_id_mapping, iort_node, 146337f6b42eSLorenzo Pieralisi iort_node->mapping_offset); 146437f6b42eSLorenzo Pieralisi 146537f6b42eSLorenzo Pieralisi for (i = 0; i < iort_node->mapping_count; i++, map++) { 146637f6b42eSLorenzo Pieralisi if (!map->output_reference) 146737f6b42eSLorenzo Pieralisi continue; 146837f6b42eSLorenzo Pieralisi 146937f6b42eSLorenzo Pieralisi parent = ACPI_ADD_PTR(struct acpi_iort_node, 147037f6b42eSLorenzo Pieralisi iort_table, map->output_reference); 147137f6b42eSLorenzo Pieralisi /* 147237f6b42eSLorenzo Pieralisi * If we detect a RC->SMMU mapping, make sure 147337f6b42eSLorenzo Pieralisi * we enable ACS on the system. 147437f6b42eSLorenzo Pieralisi */ 147537f6b42eSLorenzo Pieralisi if ((parent->type == ACPI_IORT_NODE_SMMU) || 147637f6b42eSLorenzo Pieralisi (parent->type == ACPI_IORT_NODE_SMMU_V3)) { 147737f6b42eSLorenzo Pieralisi pci_request_acs(); 147843554cebSSinan Kaya acs_enabled = true; 147943554cebSSinan Kaya return; 148037f6b42eSLorenzo Pieralisi } 148137f6b42eSLorenzo Pieralisi } 148237f6b42eSLorenzo Pieralisi } 148337f6b42eSLorenzo Pieralisi } 148443554cebSSinan Kaya #else 148543554cebSSinan Kaya static inline void iort_enable_acs(struct acpi_iort_node *iort_node) { } 148643554cebSSinan Kaya #endif 148737f6b42eSLorenzo Pieralisi 1488846f0e9eSLorenzo Pieralisi static void __init iort_init_platform_devices(void) 1489846f0e9eSLorenzo Pieralisi { 1490846f0e9eSLorenzo Pieralisi struct acpi_iort_node *iort_node, *iort_end; 1491846f0e9eSLorenzo Pieralisi struct acpi_table_iort *iort; 1492846f0e9eSLorenzo Pieralisi struct fwnode_handle *fwnode; 1493846f0e9eSLorenzo Pieralisi int i, ret; 1494896dd2c3SLorenzo Pieralisi const struct iort_dev_config *ops; 1495846f0e9eSLorenzo Pieralisi 1496846f0e9eSLorenzo Pieralisi /* 1497846f0e9eSLorenzo Pieralisi * iort_table and iort both point to the start of IORT table, but 1498846f0e9eSLorenzo Pieralisi * have different struct types 1499846f0e9eSLorenzo Pieralisi */ 1500846f0e9eSLorenzo Pieralisi iort = (struct acpi_table_iort *)iort_table; 1501846f0e9eSLorenzo Pieralisi 1502846f0e9eSLorenzo Pieralisi /* Get the first IORT node */ 1503846f0e9eSLorenzo Pieralisi iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort, 1504846f0e9eSLorenzo Pieralisi iort->node_offset); 1505846f0e9eSLorenzo Pieralisi iort_end = ACPI_ADD_PTR(struct acpi_iort_node, iort, 1506846f0e9eSLorenzo Pieralisi iort_table->length); 1507846f0e9eSLorenzo Pieralisi 1508846f0e9eSLorenzo Pieralisi for (i = 0; i < iort->node_count; i++) { 1509846f0e9eSLorenzo Pieralisi if (iort_node >= iort_end) { 1510846f0e9eSLorenzo Pieralisi pr_err("iort node pointer overflows, bad table\n"); 1511846f0e9eSLorenzo Pieralisi return; 1512846f0e9eSLorenzo Pieralisi } 1513846f0e9eSLorenzo Pieralisi 151443554cebSSinan Kaya iort_enable_acs(iort_node); 151537f6b42eSLorenzo Pieralisi 1516896dd2c3SLorenzo Pieralisi ops = iort_get_dev_cfg(iort_node); 1517896dd2c3SLorenzo Pieralisi if (ops) { 1518846f0e9eSLorenzo Pieralisi fwnode = acpi_alloc_fwnode_static(); 1519846f0e9eSLorenzo Pieralisi if (!fwnode) 1520846f0e9eSLorenzo Pieralisi return; 1521846f0e9eSLorenzo Pieralisi 1522846f0e9eSLorenzo Pieralisi iort_set_fwnode(iort_node, fwnode); 1523846f0e9eSLorenzo Pieralisi 1524896dd2c3SLorenzo Pieralisi ret = iort_add_platform_device(iort_node, ops); 1525846f0e9eSLorenzo Pieralisi if (ret) { 1526846f0e9eSLorenzo Pieralisi iort_delete_fwnode(iort_node); 1527846f0e9eSLorenzo Pieralisi acpi_free_fwnode_static(fwnode); 1528846f0e9eSLorenzo Pieralisi return; 1529846f0e9eSLorenzo Pieralisi } 1530846f0e9eSLorenzo Pieralisi } 1531846f0e9eSLorenzo Pieralisi 1532846f0e9eSLorenzo Pieralisi iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort_node, 1533846f0e9eSLorenzo Pieralisi iort_node->length); 1534846f0e9eSLorenzo Pieralisi } 1535846f0e9eSLorenzo Pieralisi } 1536846f0e9eSLorenzo Pieralisi 153788ef16d8STomasz Nowicki void __init acpi_iort_init(void) 153888ef16d8STomasz Nowicki { 153988ef16d8STomasz Nowicki acpi_status status; 154088ef16d8STomasz Nowicki 154188ef16d8STomasz Nowicki status = acpi_get_table(ACPI_SIG_IORT, 0, &iort_table); 154234ceea27SLorenzo Pieralisi if (ACPI_FAILURE(status)) { 154334ceea27SLorenzo Pieralisi if (status != AE_NOT_FOUND) { 154488ef16d8STomasz Nowicki const char *msg = acpi_format_exception(status); 154534ceea27SLorenzo Pieralisi 154688ef16d8STomasz Nowicki pr_err("Failed to get table, %s\n", msg); 154788ef16d8STomasz Nowicki } 154834ceea27SLorenzo Pieralisi 154934ceea27SLorenzo Pieralisi return; 155034ceea27SLorenzo Pieralisi } 155134ceea27SLorenzo Pieralisi 1552846f0e9eSLorenzo Pieralisi iort_init_platform_devices(); 155388ef16d8STomasz Nowicki } 1554