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; 424bf2efd2STomasz Nowicki u32 translation_id; 434bf2efd2STomasz Nowicki }; 444bf2efd2STomasz Nowicki 457936df92SLorenzo Pieralisi struct iort_fwnode { 467936df92SLorenzo Pieralisi struct list_head list; 477936df92SLorenzo Pieralisi struct acpi_iort_node *iort_node; 487936df92SLorenzo Pieralisi struct fwnode_handle *fwnode; 497936df92SLorenzo Pieralisi }; 507936df92SLorenzo Pieralisi static LIST_HEAD(iort_fwnode_list); 517936df92SLorenzo Pieralisi static DEFINE_SPINLOCK(iort_fwnode_lock); 527936df92SLorenzo Pieralisi 537936df92SLorenzo Pieralisi /** 547936df92SLorenzo Pieralisi * iort_set_fwnode() - Create iort_fwnode and use it to register 557936df92SLorenzo Pieralisi * iommu data in the iort_fwnode_list 567936df92SLorenzo Pieralisi * 577936df92SLorenzo Pieralisi * @node: IORT table node associated with the IOMMU 587936df92SLorenzo Pieralisi * @fwnode: fwnode associated with the IORT node 597936df92SLorenzo Pieralisi * 607936df92SLorenzo Pieralisi * Returns: 0 on success 617936df92SLorenzo Pieralisi * <0 on failure 627936df92SLorenzo Pieralisi */ 637936df92SLorenzo Pieralisi static inline int iort_set_fwnode(struct acpi_iort_node *iort_node, 647936df92SLorenzo Pieralisi struct fwnode_handle *fwnode) 657936df92SLorenzo Pieralisi { 667936df92SLorenzo Pieralisi struct iort_fwnode *np; 677936df92SLorenzo Pieralisi 687936df92SLorenzo Pieralisi np = kzalloc(sizeof(struct iort_fwnode), GFP_ATOMIC); 697936df92SLorenzo Pieralisi 707936df92SLorenzo Pieralisi if (WARN_ON(!np)) 717936df92SLorenzo Pieralisi return -ENOMEM; 727936df92SLorenzo Pieralisi 737936df92SLorenzo Pieralisi INIT_LIST_HEAD(&np->list); 747936df92SLorenzo Pieralisi np->iort_node = iort_node; 757936df92SLorenzo Pieralisi np->fwnode = fwnode; 767936df92SLorenzo Pieralisi 777936df92SLorenzo Pieralisi spin_lock(&iort_fwnode_lock); 787936df92SLorenzo Pieralisi list_add_tail(&np->list, &iort_fwnode_list); 797936df92SLorenzo Pieralisi spin_unlock(&iort_fwnode_lock); 807936df92SLorenzo Pieralisi 817936df92SLorenzo Pieralisi return 0; 827936df92SLorenzo Pieralisi } 837936df92SLorenzo Pieralisi 847936df92SLorenzo Pieralisi /** 857936df92SLorenzo Pieralisi * iort_get_fwnode() - Retrieve fwnode associated with an IORT node 867936df92SLorenzo Pieralisi * 877936df92SLorenzo Pieralisi * @node: IORT table node to be looked-up 887936df92SLorenzo Pieralisi * 897936df92SLorenzo Pieralisi * Returns: fwnode_handle pointer on success, NULL on failure 907936df92SLorenzo Pieralisi */ 91e3d49392SLorenzo Pieralisi static inline struct fwnode_handle *iort_get_fwnode( 92e3d49392SLorenzo Pieralisi struct acpi_iort_node *node) 937936df92SLorenzo Pieralisi { 947936df92SLorenzo Pieralisi struct iort_fwnode *curr; 957936df92SLorenzo Pieralisi struct fwnode_handle *fwnode = NULL; 967936df92SLorenzo Pieralisi 977936df92SLorenzo Pieralisi spin_lock(&iort_fwnode_lock); 987936df92SLorenzo Pieralisi list_for_each_entry(curr, &iort_fwnode_list, list) { 997936df92SLorenzo Pieralisi if (curr->iort_node == node) { 1007936df92SLorenzo Pieralisi fwnode = curr->fwnode; 1017936df92SLorenzo Pieralisi break; 1027936df92SLorenzo Pieralisi } 1037936df92SLorenzo Pieralisi } 1047936df92SLorenzo Pieralisi spin_unlock(&iort_fwnode_lock); 1057936df92SLorenzo Pieralisi 1067936df92SLorenzo Pieralisi return fwnode; 1077936df92SLorenzo Pieralisi } 1087936df92SLorenzo Pieralisi 1097936df92SLorenzo Pieralisi /** 1107936df92SLorenzo Pieralisi * iort_delete_fwnode() - Delete fwnode associated with an IORT node 1117936df92SLorenzo Pieralisi * 1127936df92SLorenzo Pieralisi * @node: IORT table node associated with fwnode to delete 1137936df92SLorenzo Pieralisi */ 1147936df92SLorenzo Pieralisi static inline void iort_delete_fwnode(struct acpi_iort_node *node) 1157936df92SLorenzo Pieralisi { 1167936df92SLorenzo Pieralisi struct iort_fwnode *curr, *tmp; 1177936df92SLorenzo Pieralisi 1187936df92SLorenzo Pieralisi spin_lock(&iort_fwnode_lock); 1197936df92SLorenzo Pieralisi list_for_each_entry_safe(curr, tmp, &iort_fwnode_list, list) { 1207936df92SLorenzo Pieralisi if (curr->iort_node == node) { 1217936df92SLorenzo Pieralisi list_del(&curr->list); 1227936df92SLorenzo Pieralisi kfree(curr); 1237936df92SLorenzo Pieralisi break; 1247936df92SLorenzo Pieralisi } 1257936df92SLorenzo Pieralisi } 1267936df92SLorenzo Pieralisi spin_unlock(&iort_fwnode_lock); 1277936df92SLorenzo Pieralisi } 1287936df92SLorenzo Pieralisi 1290a71d8b9SHanjun Guo /** 1300a71d8b9SHanjun Guo * iort_get_iort_node() - Retrieve iort_node associated with an fwnode 1310a71d8b9SHanjun Guo * 1320a71d8b9SHanjun Guo * @fwnode: fwnode associated with device to be looked-up 1330a71d8b9SHanjun Guo * 1340a71d8b9SHanjun Guo * Returns: iort_node pointer on success, NULL on failure 1350a71d8b9SHanjun Guo */ 1360a71d8b9SHanjun Guo static inline struct acpi_iort_node *iort_get_iort_node( 1370a71d8b9SHanjun Guo struct fwnode_handle *fwnode) 1380a71d8b9SHanjun Guo { 1390a71d8b9SHanjun Guo struct iort_fwnode *curr; 1400a71d8b9SHanjun Guo struct acpi_iort_node *iort_node = NULL; 1410a71d8b9SHanjun Guo 1420a71d8b9SHanjun Guo spin_lock(&iort_fwnode_lock); 1430a71d8b9SHanjun Guo list_for_each_entry(curr, &iort_fwnode_list, list) { 1440a71d8b9SHanjun Guo if (curr->fwnode == fwnode) { 1450a71d8b9SHanjun Guo iort_node = curr->iort_node; 1460a71d8b9SHanjun Guo break; 1470a71d8b9SHanjun Guo } 1480a71d8b9SHanjun Guo } 1490a71d8b9SHanjun Guo spin_unlock(&iort_fwnode_lock); 1500a71d8b9SHanjun Guo 1510a71d8b9SHanjun Guo return iort_node; 1520a71d8b9SHanjun Guo } 1530a71d8b9SHanjun Guo 15488ef16d8STomasz Nowicki typedef acpi_status (*iort_find_node_callback) 15588ef16d8STomasz Nowicki (struct acpi_iort_node *node, void *context); 15688ef16d8STomasz Nowicki 15788ef16d8STomasz Nowicki /* Root pointer to the mapped IORT table */ 15888ef16d8STomasz Nowicki static struct acpi_table_header *iort_table; 15988ef16d8STomasz Nowicki 16088ef16d8STomasz Nowicki static LIST_HEAD(iort_msi_chip_list); 16188ef16d8STomasz Nowicki static DEFINE_SPINLOCK(iort_msi_chip_lock); 16288ef16d8STomasz Nowicki 1634bf2efd2STomasz Nowicki /** 1644bf2efd2STomasz Nowicki * iort_register_domain_token() - register domain token and related ITS ID 1654bf2efd2STomasz Nowicki * to the list from where we can get it back later on. 1664bf2efd2STomasz Nowicki * @trans_id: ITS ID. 1674bf2efd2STomasz Nowicki * @fw_node: Domain token. 1684bf2efd2STomasz Nowicki * 1694bf2efd2STomasz Nowicki * Returns: 0 on success, -ENOMEM if no memory when allocating list element 1704bf2efd2STomasz Nowicki */ 1714bf2efd2STomasz Nowicki int iort_register_domain_token(int trans_id, struct fwnode_handle *fw_node) 1724bf2efd2STomasz Nowicki { 1734bf2efd2STomasz Nowicki struct iort_its_msi_chip *its_msi_chip; 1744bf2efd2STomasz Nowicki 1754bf2efd2STomasz Nowicki its_msi_chip = kzalloc(sizeof(*its_msi_chip), GFP_KERNEL); 1764bf2efd2STomasz Nowicki if (!its_msi_chip) 1774bf2efd2STomasz Nowicki return -ENOMEM; 1784bf2efd2STomasz Nowicki 1794bf2efd2STomasz Nowicki its_msi_chip->fw_node = fw_node; 1804bf2efd2STomasz Nowicki its_msi_chip->translation_id = trans_id; 1814bf2efd2STomasz Nowicki 1824bf2efd2STomasz Nowicki spin_lock(&iort_msi_chip_lock); 1834bf2efd2STomasz Nowicki list_add(&its_msi_chip->list, &iort_msi_chip_list); 1844bf2efd2STomasz Nowicki spin_unlock(&iort_msi_chip_lock); 1854bf2efd2STomasz Nowicki 1864bf2efd2STomasz Nowicki return 0; 1874bf2efd2STomasz Nowicki } 1884bf2efd2STomasz Nowicki 1894bf2efd2STomasz Nowicki /** 1904bf2efd2STomasz Nowicki * iort_deregister_domain_token() - Deregister domain token based on ITS ID 1914bf2efd2STomasz Nowicki * @trans_id: ITS ID. 1924bf2efd2STomasz Nowicki * 1934bf2efd2STomasz Nowicki * Returns: none. 1944bf2efd2STomasz Nowicki */ 1954bf2efd2STomasz Nowicki void iort_deregister_domain_token(int trans_id) 1964bf2efd2STomasz Nowicki { 1974bf2efd2STomasz Nowicki struct iort_its_msi_chip *its_msi_chip, *t; 1984bf2efd2STomasz Nowicki 1994bf2efd2STomasz Nowicki spin_lock(&iort_msi_chip_lock); 2004bf2efd2STomasz Nowicki list_for_each_entry_safe(its_msi_chip, t, &iort_msi_chip_list, list) { 2014bf2efd2STomasz Nowicki if (its_msi_chip->translation_id == trans_id) { 2024bf2efd2STomasz Nowicki list_del(&its_msi_chip->list); 2034bf2efd2STomasz Nowicki kfree(its_msi_chip); 2044bf2efd2STomasz Nowicki break; 2054bf2efd2STomasz Nowicki } 2064bf2efd2STomasz Nowicki } 2074bf2efd2STomasz Nowicki spin_unlock(&iort_msi_chip_lock); 2084bf2efd2STomasz Nowicki } 2094bf2efd2STomasz Nowicki 2104bf2efd2STomasz Nowicki /** 2114bf2efd2STomasz Nowicki * iort_find_domain_token() - Find domain token based on given ITS ID 2124bf2efd2STomasz Nowicki * @trans_id: ITS ID. 2134bf2efd2STomasz Nowicki * 2144bf2efd2STomasz Nowicki * Returns: domain token when find on the list, NULL otherwise 2154bf2efd2STomasz Nowicki */ 2164bf2efd2STomasz Nowicki struct fwnode_handle *iort_find_domain_token(int trans_id) 2174bf2efd2STomasz Nowicki { 2184bf2efd2STomasz Nowicki struct fwnode_handle *fw_node = NULL; 2194bf2efd2STomasz Nowicki struct iort_its_msi_chip *its_msi_chip; 2204bf2efd2STomasz Nowicki 2214bf2efd2STomasz Nowicki spin_lock(&iort_msi_chip_lock); 2224bf2efd2STomasz Nowicki list_for_each_entry(its_msi_chip, &iort_msi_chip_list, list) { 2234bf2efd2STomasz Nowicki if (its_msi_chip->translation_id == trans_id) { 2244bf2efd2STomasz Nowicki fw_node = its_msi_chip->fw_node; 2254bf2efd2STomasz Nowicki break; 2264bf2efd2STomasz Nowicki } 2274bf2efd2STomasz Nowicki } 2284bf2efd2STomasz Nowicki spin_unlock(&iort_msi_chip_lock); 2294bf2efd2STomasz Nowicki 2304bf2efd2STomasz Nowicki return fw_node; 2314bf2efd2STomasz Nowicki } 2324bf2efd2STomasz Nowicki 23388ef16d8STomasz Nowicki static struct acpi_iort_node *iort_scan_node(enum acpi_iort_node_type type, 23488ef16d8STomasz Nowicki iort_find_node_callback callback, 23588ef16d8STomasz Nowicki void *context) 23688ef16d8STomasz Nowicki { 23788ef16d8STomasz Nowicki struct acpi_iort_node *iort_node, *iort_end; 23888ef16d8STomasz Nowicki struct acpi_table_iort *iort; 23988ef16d8STomasz Nowicki int i; 24088ef16d8STomasz Nowicki 24188ef16d8STomasz Nowicki if (!iort_table) 24288ef16d8STomasz Nowicki return NULL; 24388ef16d8STomasz Nowicki 24488ef16d8STomasz Nowicki /* Get the first IORT node */ 24588ef16d8STomasz Nowicki iort = (struct acpi_table_iort *)iort_table; 24688ef16d8STomasz Nowicki iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort, 24788ef16d8STomasz Nowicki iort->node_offset); 24888ef16d8STomasz Nowicki iort_end = ACPI_ADD_PTR(struct acpi_iort_node, iort_table, 24988ef16d8STomasz Nowicki iort_table->length); 25088ef16d8STomasz Nowicki 25188ef16d8STomasz Nowicki for (i = 0; i < iort->node_count; i++) { 25288ef16d8STomasz Nowicki if (WARN_TAINT(iort_node >= iort_end, TAINT_FIRMWARE_WORKAROUND, 25388ef16d8STomasz Nowicki "IORT node pointer overflows, bad table!\n")) 25488ef16d8STomasz Nowicki return NULL; 25588ef16d8STomasz Nowicki 25688ef16d8STomasz Nowicki if (iort_node->type == type && 25788ef16d8STomasz Nowicki ACPI_SUCCESS(callback(iort_node, context))) 25888ef16d8STomasz Nowicki return iort_node; 25988ef16d8STomasz Nowicki 26088ef16d8STomasz Nowicki iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort_node, 26188ef16d8STomasz Nowicki iort_node->length); 26288ef16d8STomasz Nowicki } 26388ef16d8STomasz Nowicki 26488ef16d8STomasz Nowicki return NULL; 26588ef16d8STomasz Nowicki } 26688ef16d8STomasz Nowicki 26788ef16d8STomasz Nowicki static acpi_status iort_match_node_callback(struct acpi_iort_node *node, 26888ef16d8STomasz Nowicki void *context) 26988ef16d8STomasz Nowicki { 27088ef16d8STomasz Nowicki struct device *dev = context; 271c92bdfe8SHanjun Guo acpi_status status = AE_NOT_FOUND; 27288ef16d8STomasz Nowicki 27388ef16d8STomasz Nowicki if (node->type == ACPI_IORT_NODE_NAMED_COMPONENT) { 27488ef16d8STomasz Nowicki struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL }; 27588ef16d8STomasz Nowicki struct acpi_device *adev = to_acpi_device_node(dev->fwnode); 27688ef16d8STomasz Nowicki struct acpi_iort_named_component *ncomp; 27788ef16d8STomasz Nowicki 278c92bdfe8SHanjun Guo if (!adev) 27988ef16d8STomasz Nowicki goto out; 28088ef16d8STomasz Nowicki 28188ef16d8STomasz Nowicki status = acpi_get_name(adev->handle, ACPI_FULL_PATHNAME, &buf); 28288ef16d8STomasz Nowicki if (ACPI_FAILURE(status)) { 28388ef16d8STomasz Nowicki dev_warn(dev, "Can't get device full path name\n"); 28488ef16d8STomasz Nowicki goto out; 28588ef16d8STomasz Nowicki } 28688ef16d8STomasz Nowicki 28788ef16d8STomasz Nowicki ncomp = (struct acpi_iort_named_component *)node->node_data; 28888ef16d8STomasz Nowicki status = !strcmp(ncomp->device_name, buf.pointer) ? 28988ef16d8STomasz Nowicki AE_OK : AE_NOT_FOUND; 29088ef16d8STomasz Nowicki acpi_os_free(buf.pointer); 29188ef16d8STomasz Nowicki } else if (node->type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX) { 29288ef16d8STomasz Nowicki struct acpi_iort_root_complex *pci_rc; 29388ef16d8STomasz Nowicki struct pci_bus *bus; 29488ef16d8STomasz Nowicki 29588ef16d8STomasz Nowicki bus = to_pci_bus(dev); 29688ef16d8STomasz Nowicki pci_rc = (struct acpi_iort_root_complex *)node->node_data; 29788ef16d8STomasz Nowicki 29888ef16d8STomasz Nowicki /* 29988ef16d8STomasz Nowicki * It is assumed that PCI segment numbers maps one-to-one 30088ef16d8STomasz Nowicki * with root complexes. Each segment number can represent only 30188ef16d8STomasz Nowicki * one root complex. 30288ef16d8STomasz Nowicki */ 30388ef16d8STomasz Nowicki status = pci_rc->pci_segment_number == pci_domain_nr(bus) ? 30488ef16d8STomasz Nowicki AE_OK : AE_NOT_FOUND; 30588ef16d8STomasz Nowicki } 30688ef16d8STomasz Nowicki out: 30788ef16d8STomasz Nowicki return status; 30888ef16d8STomasz Nowicki } 30988ef16d8STomasz Nowicki 31088ef16d8STomasz Nowicki static int iort_id_map(struct acpi_iort_id_mapping *map, u8 type, u32 rid_in, 31188ef16d8STomasz Nowicki u32 *rid_out) 31288ef16d8STomasz Nowicki { 31388ef16d8STomasz Nowicki /* Single mapping does not care for input id */ 31488ef16d8STomasz Nowicki if (map->flags & ACPI_IORT_ID_SINGLE_MAPPING) { 31588ef16d8STomasz Nowicki if (type == ACPI_IORT_NODE_NAMED_COMPONENT || 31688ef16d8STomasz Nowicki type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX) { 31788ef16d8STomasz Nowicki *rid_out = map->output_base; 31888ef16d8STomasz Nowicki return 0; 31988ef16d8STomasz Nowicki } 32088ef16d8STomasz Nowicki 32188ef16d8STomasz Nowicki pr_warn(FW_BUG "[map %p] SINGLE MAPPING flag not allowed for node type %d, skipping ID map\n", 32288ef16d8STomasz Nowicki map, type); 32388ef16d8STomasz Nowicki return -ENXIO; 32488ef16d8STomasz Nowicki } 32588ef16d8STomasz Nowicki 32688ef16d8STomasz Nowicki if (rid_in < map->input_base || 32788ef16d8STomasz Nowicki (rid_in >= map->input_base + map->id_count)) 32888ef16d8STomasz Nowicki return -ENXIO; 32988ef16d8STomasz Nowicki 33088ef16d8STomasz Nowicki *rid_out = map->output_base + (rid_in - map->input_base); 33188ef16d8STomasz Nowicki return 0; 33288ef16d8STomasz Nowicki } 33388ef16d8STomasz Nowicki 334e3d49392SLorenzo Pieralisi static struct acpi_iort_node *iort_node_get_id(struct acpi_iort_node *node, 3358ca4f1d3SHanjun Guo u32 *id_out, int index) 336618f535aSLorenzo Pieralisi { 337618f535aSLorenzo Pieralisi struct acpi_iort_node *parent; 338618f535aSLorenzo Pieralisi struct acpi_iort_id_mapping *map; 339618f535aSLorenzo Pieralisi 340618f535aSLorenzo Pieralisi if (!node->mapping_offset || !node->mapping_count || 341618f535aSLorenzo Pieralisi index >= node->mapping_count) 342618f535aSLorenzo Pieralisi return NULL; 343618f535aSLorenzo Pieralisi 344618f535aSLorenzo Pieralisi map = ACPI_ADD_PTR(struct acpi_iort_id_mapping, node, 345030abd8aSLorenzo Pieralisi node->mapping_offset + index * sizeof(*map)); 346618f535aSLorenzo Pieralisi 347618f535aSLorenzo Pieralisi /* Firmware bug! */ 348618f535aSLorenzo Pieralisi if (!map->output_reference) { 349618f535aSLorenzo Pieralisi pr_err(FW_BUG "[node %p type %d] ID map has NULL parent reference\n", 350618f535aSLorenzo Pieralisi node, node->type); 351618f535aSLorenzo Pieralisi return NULL; 352618f535aSLorenzo Pieralisi } 353618f535aSLorenzo Pieralisi 354618f535aSLorenzo Pieralisi parent = ACPI_ADD_PTR(struct acpi_iort_node, iort_table, 355618f535aSLorenzo Pieralisi map->output_reference); 356618f535aSLorenzo Pieralisi 357030abd8aSLorenzo Pieralisi if (map->flags & ACPI_IORT_ID_SINGLE_MAPPING) { 358618f535aSLorenzo Pieralisi if (node->type == ACPI_IORT_NODE_NAMED_COMPONENT || 359618f535aSLorenzo Pieralisi node->type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX) { 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 368*8c8df8dcSHanjun Guo static inline int iort_get_id_mapping_index(struct acpi_iort_node *node) 369*8c8df8dcSHanjun Guo { 370*8c8df8dcSHanjun Guo return -EINVAL; 371*8c8df8dcSHanjun Guo } 372*8c8df8dcSHanjun Guo 373697f6093SHanjun Guo static struct acpi_iort_node *iort_node_map_id(struct acpi_iort_node *node, 374697f6093SHanjun Guo u32 id_in, u32 *id_out, 375ea50b524SLorenzo Pieralisi u8 type_mask) 37688ef16d8STomasz Nowicki { 377697f6093SHanjun Guo u32 id = id_in; 37888ef16d8STomasz Nowicki 37988ef16d8STomasz Nowicki /* Parse the ID mapping tree to find specified node type */ 38088ef16d8STomasz Nowicki while (node) { 38188ef16d8STomasz Nowicki struct acpi_iort_id_mapping *map; 382*8c8df8dcSHanjun Guo int i, index; 38388ef16d8STomasz Nowicki 384ea50b524SLorenzo Pieralisi if (IORT_TYPE_MASK(node->type) & type_mask) { 385697f6093SHanjun Guo if (id_out) 386697f6093SHanjun Guo *id_out = id; 38788ef16d8STomasz Nowicki return node; 38888ef16d8STomasz Nowicki } 38988ef16d8STomasz Nowicki 39088ef16d8STomasz Nowicki if (!node->mapping_offset || !node->mapping_count) 39188ef16d8STomasz Nowicki goto fail_map; 39288ef16d8STomasz Nowicki 39388ef16d8STomasz Nowicki map = ACPI_ADD_PTR(struct acpi_iort_id_mapping, node, 39488ef16d8STomasz Nowicki node->mapping_offset); 39588ef16d8STomasz Nowicki 39688ef16d8STomasz Nowicki /* Firmware bug! */ 39788ef16d8STomasz Nowicki if (!map->output_reference) { 39888ef16d8STomasz Nowicki pr_err(FW_BUG "[node %p type %d] ID map has NULL parent reference\n", 39988ef16d8STomasz Nowicki node, node->type); 40088ef16d8STomasz Nowicki goto fail_map; 40188ef16d8STomasz Nowicki } 40288ef16d8STomasz Nowicki 403*8c8df8dcSHanjun Guo /* 404*8c8df8dcSHanjun Guo * Get the special ID mapping index (if any) and skip its 405*8c8df8dcSHanjun Guo * associated ID map to prevent erroneous multi-stage 406*8c8df8dcSHanjun Guo * IORT ID translations. 407*8c8df8dcSHanjun Guo */ 408*8c8df8dcSHanjun Guo index = iort_get_id_mapping_index(node); 409*8c8df8dcSHanjun Guo 410697f6093SHanjun Guo /* Do the ID translation */ 41188ef16d8STomasz Nowicki for (i = 0; i < node->mapping_count; i++, map++) { 412*8c8df8dcSHanjun Guo /* if it is special mapping index, skip it */ 413*8c8df8dcSHanjun Guo if (i == index) 414*8c8df8dcSHanjun Guo continue; 415*8c8df8dcSHanjun Guo 416697f6093SHanjun Guo if (!iort_id_map(map, node->type, id, &id)) 41788ef16d8STomasz Nowicki break; 41888ef16d8STomasz Nowicki } 41988ef16d8STomasz Nowicki 42088ef16d8STomasz Nowicki if (i == node->mapping_count) 42188ef16d8STomasz Nowicki goto fail_map; 42288ef16d8STomasz Nowicki 42388ef16d8STomasz Nowicki node = ACPI_ADD_PTR(struct acpi_iort_node, iort_table, 42488ef16d8STomasz Nowicki map->output_reference); 42588ef16d8STomasz Nowicki } 42688ef16d8STomasz Nowicki 42788ef16d8STomasz Nowicki fail_map: 428697f6093SHanjun Guo /* Map input ID to output ID unchanged on mapping failure */ 429697f6093SHanjun Guo if (id_out) 430697f6093SHanjun Guo *id_out = id_in; 43188ef16d8STomasz Nowicki 43288ef16d8STomasz Nowicki return NULL; 43388ef16d8STomasz Nowicki } 43488ef16d8STomasz Nowicki 435e3d49392SLorenzo Pieralisi static struct acpi_iort_node *iort_node_map_platform_id( 436e3d49392SLorenzo Pieralisi struct acpi_iort_node *node, u32 *id_out, u8 type_mask, 4378ca4f1d3SHanjun Guo int index) 4388ca4f1d3SHanjun Guo { 4398ca4f1d3SHanjun Guo struct acpi_iort_node *parent; 4408ca4f1d3SHanjun Guo u32 id; 4418ca4f1d3SHanjun Guo 4428ca4f1d3SHanjun Guo /* step 1: retrieve the initial dev id */ 4438ca4f1d3SHanjun Guo parent = iort_node_get_id(node, &id, index); 4448ca4f1d3SHanjun Guo if (!parent) 4458ca4f1d3SHanjun Guo return NULL; 4468ca4f1d3SHanjun Guo 4478ca4f1d3SHanjun Guo /* 4488ca4f1d3SHanjun Guo * optional step 2: map the initial dev id if its parent is not 4498ca4f1d3SHanjun Guo * the target type we want, map it again for the use cases such 4508ca4f1d3SHanjun Guo * as NC (named component) -> SMMU -> ITS. If the type is matched, 4518ca4f1d3SHanjun Guo * return the initial dev id and its parent pointer directly. 4528ca4f1d3SHanjun Guo */ 4538ca4f1d3SHanjun Guo if (!(IORT_TYPE_MASK(parent->type) & type_mask)) 4548ca4f1d3SHanjun Guo parent = iort_node_map_id(parent, id, id_out, type_mask); 4558ca4f1d3SHanjun Guo else 4568ca4f1d3SHanjun Guo if (id_out) 4578ca4f1d3SHanjun Guo *id_out = id; 4588ca4f1d3SHanjun Guo 4598ca4f1d3SHanjun Guo return parent; 4608ca4f1d3SHanjun Guo } 4618ca4f1d3SHanjun Guo 46288ef16d8STomasz Nowicki static struct acpi_iort_node *iort_find_dev_node(struct device *dev) 46388ef16d8STomasz Nowicki { 46488ef16d8STomasz Nowicki struct pci_bus *pbus; 46588ef16d8STomasz Nowicki 4660a71d8b9SHanjun Guo if (!dev_is_pci(dev)) { 4670a71d8b9SHanjun Guo struct acpi_iort_node *node; 4680a71d8b9SHanjun Guo /* 4690a71d8b9SHanjun Guo * scan iort_fwnode_list to see if it's an iort platform 4700a71d8b9SHanjun Guo * device (such as SMMU, PMCG),its iort node already cached 4710a71d8b9SHanjun Guo * and associated with fwnode when iort platform devices 4720a71d8b9SHanjun Guo * were initialized. 4730a71d8b9SHanjun Guo */ 4740a71d8b9SHanjun Guo node = iort_get_iort_node(dev->fwnode); 4750a71d8b9SHanjun Guo if (node) 4760a71d8b9SHanjun Guo return node; 4770a71d8b9SHanjun Guo 4780a71d8b9SHanjun Guo /* 4790a71d8b9SHanjun Guo * if not, then it should be a platform device defined in 4800a71d8b9SHanjun Guo * DSDT/SSDT (with Named Component node in IORT) 4810a71d8b9SHanjun Guo */ 48288ef16d8STomasz Nowicki return iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT, 48388ef16d8STomasz Nowicki iort_match_node_callback, dev); 4840a71d8b9SHanjun Guo } 48588ef16d8STomasz Nowicki 48688ef16d8STomasz Nowicki /* Find a PCI root bus */ 48788ef16d8STomasz Nowicki pbus = to_pci_dev(dev)->bus; 48888ef16d8STomasz Nowicki while (!pci_is_root_bus(pbus)) 48988ef16d8STomasz Nowicki pbus = pbus->parent; 49088ef16d8STomasz Nowicki 49188ef16d8STomasz Nowicki return iort_scan_node(ACPI_IORT_NODE_PCI_ROOT_COMPLEX, 49288ef16d8STomasz Nowicki iort_match_node_callback, &pbus->dev); 49388ef16d8STomasz Nowicki } 49488ef16d8STomasz Nowicki 4954bf2efd2STomasz Nowicki /** 4964bf2efd2STomasz Nowicki * iort_msi_map_rid() - Map a MSI requester ID for a device 4974bf2efd2STomasz Nowicki * @dev: The device for which the mapping is to be done. 4984bf2efd2STomasz Nowicki * @req_id: The device requester ID. 4994bf2efd2STomasz Nowicki * 5004bf2efd2STomasz Nowicki * Returns: mapped MSI RID on success, input requester ID otherwise 5014bf2efd2STomasz Nowicki */ 5024bf2efd2STomasz Nowicki u32 iort_msi_map_rid(struct device *dev, u32 req_id) 5034bf2efd2STomasz Nowicki { 5044bf2efd2STomasz Nowicki struct acpi_iort_node *node; 5054bf2efd2STomasz Nowicki u32 dev_id; 5064bf2efd2STomasz Nowicki 5074bf2efd2STomasz Nowicki node = iort_find_dev_node(dev); 5084bf2efd2STomasz Nowicki if (!node) 5094bf2efd2STomasz Nowicki return req_id; 5104bf2efd2STomasz Nowicki 511697f6093SHanjun Guo iort_node_map_id(node, req_id, &dev_id, IORT_MSI_TYPE); 5124bf2efd2STomasz Nowicki return dev_id; 5134bf2efd2STomasz Nowicki } 5144bf2efd2STomasz Nowicki 5154bf2efd2STomasz Nowicki /** 516ae7c1838SHanjun Guo * iort_pmsi_get_dev_id() - Get the device id for a device 517ae7c1838SHanjun Guo * @dev: The device for which the mapping is to be done. 518ae7c1838SHanjun Guo * @dev_id: The device ID found. 519ae7c1838SHanjun Guo * 520ae7c1838SHanjun Guo * Returns: 0 for successful find a dev id, -ENODEV on error 521ae7c1838SHanjun Guo */ 522ae7c1838SHanjun Guo int iort_pmsi_get_dev_id(struct device *dev, u32 *dev_id) 523ae7c1838SHanjun Guo { 524*8c8df8dcSHanjun Guo int i, index; 525ae7c1838SHanjun Guo struct acpi_iort_node *node; 526ae7c1838SHanjun Guo 527ae7c1838SHanjun Guo node = iort_find_dev_node(dev); 528ae7c1838SHanjun Guo if (!node) 529ae7c1838SHanjun Guo return -ENODEV; 530ae7c1838SHanjun Guo 531*8c8df8dcSHanjun Guo index = iort_get_id_mapping_index(node); 532*8c8df8dcSHanjun Guo /* if there is a valid index, go get the dev_id directly */ 533*8c8df8dcSHanjun Guo if (index >= 0) { 534*8c8df8dcSHanjun Guo if (iort_node_get_id(node, dev_id, index)) 535ae7c1838SHanjun Guo return 0; 536*8c8df8dcSHanjun Guo } else { 537*8c8df8dcSHanjun Guo for (i = 0; i < node->mapping_count; i++) { 538*8c8df8dcSHanjun Guo if (iort_node_map_platform_id(node, dev_id, 539*8c8df8dcSHanjun Guo IORT_MSI_TYPE, i)) 540*8c8df8dcSHanjun Guo return 0; 541*8c8df8dcSHanjun Guo } 542ae7c1838SHanjun Guo } 543ae7c1838SHanjun Guo 544ae7c1838SHanjun Guo return -ENODEV; 545ae7c1838SHanjun Guo } 546ae7c1838SHanjun Guo 547ae7c1838SHanjun Guo /** 5484bf2efd2STomasz Nowicki * iort_dev_find_its_id() - Find the ITS identifier for a device 5494bf2efd2STomasz Nowicki * @dev: The device. 5506cb6bf56SHanjun Guo * @req_id: Device's requester ID 5514bf2efd2STomasz Nowicki * @idx: Index of the ITS identifier list. 5524bf2efd2STomasz Nowicki * @its_id: ITS identifier. 5534bf2efd2STomasz Nowicki * 5544bf2efd2STomasz Nowicki * Returns: 0 on success, appropriate error value otherwise 5554bf2efd2STomasz Nowicki */ 5564bf2efd2STomasz Nowicki static int iort_dev_find_its_id(struct device *dev, u32 req_id, 5574bf2efd2STomasz Nowicki unsigned int idx, int *its_id) 5584bf2efd2STomasz Nowicki { 5594bf2efd2STomasz Nowicki struct acpi_iort_its_group *its; 5604bf2efd2STomasz Nowicki struct acpi_iort_node *node; 5614bf2efd2STomasz Nowicki 5624bf2efd2STomasz Nowicki node = iort_find_dev_node(dev); 5634bf2efd2STomasz Nowicki if (!node) 5644bf2efd2STomasz Nowicki return -ENXIO; 5654bf2efd2STomasz Nowicki 566697f6093SHanjun Guo node = iort_node_map_id(node, req_id, NULL, IORT_MSI_TYPE); 5674bf2efd2STomasz Nowicki if (!node) 5684bf2efd2STomasz Nowicki return -ENXIO; 5694bf2efd2STomasz Nowicki 5704bf2efd2STomasz Nowicki /* Move to ITS specific data */ 5714bf2efd2STomasz Nowicki its = (struct acpi_iort_its_group *)node->node_data; 5724bf2efd2STomasz Nowicki if (idx > its->its_count) { 5734bf2efd2STomasz Nowicki dev_err(dev, "requested ITS ID index [%d] is greater than available [%d]\n", 5744bf2efd2STomasz Nowicki idx, its->its_count); 5754bf2efd2STomasz Nowicki return -ENXIO; 5764bf2efd2STomasz Nowicki } 5774bf2efd2STomasz Nowicki 5784bf2efd2STomasz Nowicki *its_id = its->identifiers[idx]; 5794bf2efd2STomasz Nowicki return 0; 5804bf2efd2STomasz Nowicki } 5814bf2efd2STomasz Nowicki 5824bf2efd2STomasz Nowicki /** 5834bf2efd2STomasz Nowicki * iort_get_device_domain() - Find MSI domain related to a device 5844bf2efd2STomasz Nowicki * @dev: The device. 5854bf2efd2STomasz Nowicki * @req_id: Requester ID for the device. 5864bf2efd2STomasz Nowicki * 5874bf2efd2STomasz Nowicki * Returns: the MSI domain for this device, NULL otherwise 5884bf2efd2STomasz Nowicki */ 5894bf2efd2STomasz Nowicki struct irq_domain *iort_get_device_domain(struct device *dev, u32 req_id) 5904bf2efd2STomasz Nowicki { 5914bf2efd2STomasz Nowicki struct fwnode_handle *handle; 5924bf2efd2STomasz Nowicki int its_id; 5934bf2efd2STomasz Nowicki 5944bf2efd2STomasz Nowicki if (iort_dev_find_its_id(dev, req_id, 0, &its_id)) 5954bf2efd2STomasz Nowicki return NULL; 5964bf2efd2STomasz Nowicki 5974bf2efd2STomasz Nowicki handle = iort_find_domain_token(its_id); 5984bf2efd2STomasz Nowicki if (!handle) 5994bf2efd2STomasz Nowicki return NULL; 6004bf2efd2STomasz Nowicki 6014bf2efd2STomasz Nowicki return irq_find_matching_fwnode(handle, DOMAIN_BUS_PCI_MSI); 6024bf2efd2STomasz Nowicki } 6034bf2efd2STomasz Nowicki 604d4f54a18SHanjun Guo /** 605d4f54a18SHanjun Guo * iort_get_platform_device_domain() - Find MSI domain related to a 606d4f54a18SHanjun Guo * platform device 607d4f54a18SHanjun Guo * @dev: the dev pointer associated with the platform device 608d4f54a18SHanjun Guo * 609d4f54a18SHanjun Guo * Returns: the MSI domain for this device, NULL otherwise 610d4f54a18SHanjun Guo */ 611d4f54a18SHanjun Guo static struct irq_domain *iort_get_platform_device_domain(struct device *dev) 612d4f54a18SHanjun Guo { 613d4f54a18SHanjun Guo struct acpi_iort_node *node, *msi_parent; 614d4f54a18SHanjun Guo struct fwnode_handle *iort_fwnode; 615d4f54a18SHanjun Guo struct acpi_iort_its_group *its; 616d4f54a18SHanjun Guo int i; 617d4f54a18SHanjun Guo 618d4f54a18SHanjun Guo /* find its associated iort node */ 619d4f54a18SHanjun Guo node = iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT, 620d4f54a18SHanjun Guo iort_match_node_callback, dev); 621d4f54a18SHanjun Guo if (!node) 622d4f54a18SHanjun Guo return NULL; 623d4f54a18SHanjun Guo 624d4f54a18SHanjun Guo /* then find its msi parent node */ 625d4f54a18SHanjun Guo for (i = 0; i < node->mapping_count; i++) { 626d4f54a18SHanjun Guo msi_parent = iort_node_map_platform_id(node, NULL, 627d4f54a18SHanjun Guo IORT_MSI_TYPE, i); 628d4f54a18SHanjun Guo if (msi_parent) 629d4f54a18SHanjun Guo break; 630d4f54a18SHanjun Guo } 631d4f54a18SHanjun Guo 632d4f54a18SHanjun Guo if (!msi_parent) 633d4f54a18SHanjun Guo return NULL; 634d4f54a18SHanjun Guo 635d4f54a18SHanjun Guo /* Move to ITS specific data */ 636d4f54a18SHanjun Guo its = (struct acpi_iort_its_group *)msi_parent->node_data; 637d4f54a18SHanjun Guo 638d4f54a18SHanjun Guo iort_fwnode = iort_find_domain_token(its->identifiers[0]); 639d4f54a18SHanjun Guo if (!iort_fwnode) 640d4f54a18SHanjun Guo return NULL; 641d4f54a18SHanjun Guo 642d4f54a18SHanjun Guo return irq_find_matching_fwnode(iort_fwnode, DOMAIN_BUS_PLATFORM_MSI); 643d4f54a18SHanjun Guo } 644d4f54a18SHanjun Guo 645d4f54a18SHanjun Guo void acpi_configure_pmsi_domain(struct device *dev) 646d4f54a18SHanjun Guo { 647d4f54a18SHanjun Guo struct irq_domain *msi_domain; 648d4f54a18SHanjun Guo 649d4f54a18SHanjun Guo msi_domain = iort_get_platform_device_domain(dev); 650d4f54a18SHanjun Guo if (msi_domain) 651d4f54a18SHanjun Guo dev_set_msi_domain(dev, msi_domain); 652d4f54a18SHanjun Guo } 653d4f54a18SHanjun Guo 654bc8648d4SRobin Murphy static int __maybe_unused __get_pci_rid(struct pci_dev *pdev, u16 alias, 655bc8648d4SRobin Murphy void *data) 656643b8e4dSLorenzo Pieralisi { 657643b8e4dSLorenzo Pieralisi u32 *rid = data; 658643b8e4dSLorenzo Pieralisi 659643b8e4dSLorenzo Pieralisi *rid = alias; 660643b8e4dSLorenzo Pieralisi return 0; 661643b8e4dSLorenzo Pieralisi } 662643b8e4dSLorenzo Pieralisi 663643b8e4dSLorenzo Pieralisi static int arm_smmu_iort_xlate(struct device *dev, u32 streamid, 664643b8e4dSLorenzo Pieralisi struct fwnode_handle *fwnode, 665643b8e4dSLorenzo Pieralisi const struct iommu_ops *ops) 666643b8e4dSLorenzo Pieralisi { 667643b8e4dSLorenzo Pieralisi int ret = iommu_fwspec_init(dev, fwnode, ops); 668643b8e4dSLorenzo Pieralisi 669643b8e4dSLorenzo Pieralisi if (!ret) 670643b8e4dSLorenzo Pieralisi ret = iommu_fwspec_add_ids(dev, &streamid, 1); 671643b8e4dSLorenzo Pieralisi 672643b8e4dSLorenzo Pieralisi return ret; 673643b8e4dSLorenzo Pieralisi } 674643b8e4dSLorenzo Pieralisi 6751d9029d4SLorenzo Pieralisi static inline bool iort_iommu_driver_enabled(u8 type) 6761d9029d4SLorenzo Pieralisi { 6771d9029d4SLorenzo Pieralisi switch (type) { 6781d9029d4SLorenzo Pieralisi case ACPI_IORT_NODE_SMMU_V3: 6791d9029d4SLorenzo Pieralisi return IS_BUILTIN(CONFIG_ARM_SMMU_V3); 6801d9029d4SLorenzo Pieralisi case ACPI_IORT_NODE_SMMU: 6811d9029d4SLorenzo Pieralisi return IS_BUILTIN(CONFIG_ARM_SMMU); 6821d9029d4SLorenzo Pieralisi default: 6831d9029d4SLorenzo Pieralisi pr_warn("IORT node type %u does not describe an SMMU\n", type); 6841d9029d4SLorenzo Pieralisi return false; 6851d9029d4SLorenzo Pieralisi } 6861d9029d4SLorenzo Pieralisi } 6871d9029d4SLorenzo Pieralisi 688d49f2dedSLorenzo Pieralisi #ifdef CONFIG_IOMMU_API 689e3d49392SLorenzo Pieralisi static inline const struct iommu_ops *iort_fwspec_iommu_ops( 690e3d49392SLorenzo Pieralisi struct iommu_fwspec *fwspec) 691d49f2dedSLorenzo Pieralisi { 692d49f2dedSLorenzo Pieralisi return (fwspec && fwspec->ops) ? fwspec->ops : NULL; 693d49f2dedSLorenzo Pieralisi } 694d49f2dedSLorenzo Pieralisi 695e3d49392SLorenzo Pieralisi static inline int iort_add_device_replay(const struct iommu_ops *ops, 696e3d49392SLorenzo Pieralisi struct device *dev) 697d49f2dedSLorenzo Pieralisi { 698d49f2dedSLorenzo Pieralisi int err = 0; 699d49f2dedSLorenzo Pieralisi 700bc8648d4SRobin Murphy if (ops->add_device && dev->bus && !dev->iommu_group) 701d49f2dedSLorenzo Pieralisi err = ops->add_device(dev); 702d49f2dedSLorenzo Pieralisi 703d49f2dedSLorenzo Pieralisi return err; 704d49f2dedSLorenzo Pieralisi } 705d49f2dedSLorenzo Pieralisi #else 706e3d49392SLorenzo Pieralisi static inline const struct iommu_ops *iort_fwspec_iommu_ops( 707e3d49392SLorenzo Pieralisi struct iommu_fwspec *fwspec) 708d49f2dedSLorenzo Pieralisi { return NULL; } 709e3d49392SLorenzo Pieralisi static inline int iort_add_device_replay(const struct iommu_ops *ops, 710e3d49392SLorenzo Pieralisi struct device *dev) 711d49f2dedSLorenzo Pieralisi { return 0; } 712d49f2dedSLorenzo Pieralisi #endif 713d49f2dedSLorenzo Pieralisi 714bc8648d4SRobin Murphy static int iort_iommu_xlate(struct device *dev, struct acpi_iort_node *node, 715643b8e4dSLorenzo Pieralisi u32 streamid) 716643b8e4dSLorenzo Pieralisi { 717bc8648d4SRobin Murphy const struct iommu_ops *ops; 718643b8e4dSLorenzo Pieralisi struct fwnode_handle *iort_fwnode; 719643b8e4dSLorenzo Pieralisi 720bc8648d4SRobin Murphy if (!node) 721bc8648d4SRobin Murphy return -ENODEV; 722bc8648d4SRobin Murphy 723643b8e4dSLorenzo Pieralisi iort_fwnode = iort_get_fwnode(node); 724643b8e4dSLorenzo Pieralisi if (!iort_fwnode) 725bc8648d4SRobin Murphy return -ENODEV; 726643b8e4dSLorenzo Pieralisi 7275a1bb638SSricharan R /* 7285a1bb638SSricharan R * If the ops look-up fails, this means that either 7295a1bb638SSricharan R * the SMMU drivers have not been probed yet or that 7305a1bb638SSricharan R * the SMMU drivers are not built in the kernel; 7315a1bb638SSricharan R * Depending on whether the SMMU drivers are built-in 7325a1bb638SSricharan R * in the kernel or not, defer the IOMMU configuration 7335a1bb638SSricharan R * or just abort it. 7345a1bb638SSricharan R */ 735bc8648d4SRobin Murphy ops = iommu_ops_from_fwnode(iort_fwnode); 736643b8e4dSLorenzo Pieralisi if (!ops) 7375a1bb638SSricharan R return iort_iommu_driver_enabled(node->type) ? 738bc8648d4SRobin Murphy -EPROBE_DEFER : -ENODEV; 739643b8e4dSLorenzo Pieralisi 740bc8648d4SRobin Murphy return arm_smmu_iort_xlate(dev, streamid, iort_fwnode, ops); 741643b8e4dSLorenzo Pieralisi } 742643b8e4dSLorenzo Pieralisi 743bc8648d4SRobin Murphy struct iort_pci_alias_info { 744bc8648d4SRobin Murphy struct device *dev; 745bc8648d4SRobin Murphy struct acpi_iort_node *node; 746bc8648d4SRobin Murphy }; 747bc8648d4SRobin Murphy 748bc8648d4SRobin Murphy static int iort_pci_iommu_init(struct pci_dev *pdev, u16 alias, void *data) 749bc8648d4SRobin Murphy { 750bc8648d4SRobin Murphy struct iort_pci_alias_info *info = data; 751bc8648d4SRobin Murphy struct acpi_iort_node *parent; 752bc8648d4SRobin Murphy u32 streamid; 753bc8648d4SRobin Murphy 754bc8648d4SRobin Murphy parent = iort_node_map_id(info->node, alias, &streamid, 755bc8648d4SRobin Murphy IORT_IOMMU_TYPE); 756bc8648d4SRobin Murphy return iort_iommu_xlate(info->dev, parent, streamid); 757643b8e4dSLorenzo Pieralisi } 758643b8e4dSLorenzo Pieralisi 75910d8ab2cSLorenzo Pieralisi static int nc_dma_get_range(struct device *dev, u64 *size) 76010d8ab2cSLorenzo Pieralisi { 76110d8ab2cSLorenzo Pieralisi struct acpi_iort_node *node; 76210d8ab2cSLorenzo Pieralisi struct acpi_iort_named_component *ncomp; 76310d8ab2cSLorenzo Pieralisi 76410d8ab2cSLorenzo Pieralisi node = iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT, 76510d8ab2cSLorenzo Pieralisi iort_match_node_callback, dev); 76610d8ab2cSLorenzo Pieralisi if (!node) 76710d8ab2cSLorenzo Pieralisi return -ENODEV; 76810d8ab2cSLorenzo Pieralisi 76910d8ab2cSLorenzo Pieralisi ncomp = (struct acpi_iort_named_component *)node->node_data; 77010d8ab2cSLorenzo Pieralisi 77110d8ab2cSLorenzo Pieralisi *size = ncomp->memory_address_limit >= 64 ? U64_MAX : 77210d8ab2cSLorenzo Pieralisi 1ULL<<ncomp->memory_address_limit; 77310d8ab2cSLorenzo Pieralisi 77410d8ab2cSLorenzo Pieralisi return 0; 77510d8ab2cSLorenzo Pieralisi } 77610d8ab2cSLorenzo Pieralisi 777643b8e4dSLorenzo Pieralisi /** 7787ad42639SLorenzo Pieralisi * iort_dma_setup() - Set-up device DMA parameters. 77918b709beSLorenzo Pieralisi * 78018b709beSLorenzo Pieralisi * @dev: device to configure 7817ad42639SLorenzo Pieralisi * @dma_addr: device DMA address result pointer 7827ad42639SLorenzo Pieralisi * @size: DMA range size result pointer 78318b709beSLorenzo Pieralisi */ 7847ad42639SLorenzo Pieralisi void iort_dma_setup(struct device *dev, u64 *dma_addr, u64 *dma_size) 78518b709beSLorenzo Pieralisi { 7867ad42639SLorenzo Pieralisi u64 mask, dmaaddr = 0, size = 0, offset = 0; 7877ad42639SLorenzo Pieralisi int ret, msb; 7887ad42639SLorenzo Pieralisi 78918b709beSLorenzo Pieralisi /* 79018b709beSLorenzo Pieralisi * Set default coherent_dma_mask to 32 bit. Drivers are expected to 79118b709beSLorenzo Pieralisi * setup the correct supported mask. 79218b709beSLorenzo Pieralisi */ 79318b709beSLorenzo Pieralisi if (!dev->coherent_dma_mask) 79418b709beSLorenzo Pieralisi dev->coherent_dma_mask = DMA_BIT_MASK(32); 79518b709beSLorenzo Pieralisi 79618b709beSLorenzo Pieralisi /* 79718b709beSLorenzo Pieralisi * Set it to coherent_dma_mask by default if the architecture 79818b709beSLorenzo Pieralisi * code has not set it. 79918b709beSLorenzo Pieralisi */ 80018b709beSLorenzo Pieralisi if (!dev->dma_mask) 80118b709beSLorenzo Pieralisi dev->dma_mask = &dev->coherent_dma_mask; 8027ad42639SLorenzo Pieralisi 8037ad42639SLorenzo Pieralisi size = max(dev->coherent_dma_mask, dev->coherent_dma_mask + 1); 8047ad42639SLorenzo Pieralisi 80510d8ab2cSLorenzo Pieralisi if (dev_is_pci(dev)) 8067ad42639SLorenzo Pieralisi ret = acpi_dma_get_range(dev, &dmaaddr, &offset, &size); 80710d8ab2cSLorenzo Pieralisi else 80810d8ab2cSLorenzo Pieralisi ret = nc_dma_get_range(dev, &size); 80910d8ab2cSLorenzo Pieralisi 8107ad42639SLorenzo Pieralisi if (!ret) { 8117ad42639SLorenzo Pieralisi msb = fls64(dmaaddr + size - 1); 8127ad42639SLorenzo Pieralisi /* 8137ad42639SLorenzo Pieralisi * Round-up to the power-of-two mask or set 8147ad42639SLorenzo Pieralisi * the mask to the whole 64-bit address space 8157ad42639SLorenzo Pieralisi * in case the DMA region covers the full 8167ad42639SLorenzo Pieralisi * memory window. 8177ad42639SLorenzo Pieralisi */ 8187ad42639SLorenzo Pieralisi mask = msb == 64 ? U64_MAX : (1ULL << msb) - 1; 8197ad42639SLorenzo Pieralisi /* 8207ad42639SLorenzo Pieralisi * Limit coherent and dma mask based on size 8217ad42639SLorenzo Pieralisi * retrieved from firmware. 8227ad42639SLorenzo Pieralisi */ 8237ad42639SLorenzo Pieralisi dev->coherent_dma_mask = mask; 8247ad42639SLorenzo Pieralisi *dev->dma_mask = mask; 8257ad42639SLorenzo Pieralisi } 8267ad42639SLorenzo Pieralisi 8277ad42639SLorenzo Pieralisi *dma_addr = dmaaddr; 8287ad42639SLorenzo Pieralisi *dma_size = size; 8297ad42639SLorenzo Pieralisi 8307ad42639SLorenzo Pieralisi dev->dma_pfn_offset = PFN_DOWN(offset); 8317ad42639SLorenzo Pieralisi dev_dbg(dev, "dma_pfn_offset(%#08llx)\n", offset); 83218b709beSLorenzo Pieralisi } 83318b709beSLorenzo Pieralisi 83418b709beSLorenzo Pieralisi /** 835643b8e4dSLorenzo Pieralisi * iort_iommu_configure - Set-up IOMMU configuration for a device. 836643b8e4dSLorenzo Pieralisi * 837643b8e4dSLorenzo Pieralisi * @dev: device to configure 838643b8e4dSLorenzo Pieralisi * 839643b8e4dSLorenzo Pieralisi * Returns: iommu_ops pointer on configuration success 840643b8e4dSLorenzo Pieralisi * NULL on configuration failure 841643b8e4dSLorenzo Pieralisi */ 842643b8e4dSLorenzo Pieralisi const struct iommu_ops *iort_iommu_configure(struct device *dev) 843643b8e4dSLorenzo Pieralisi { 844643b8e4dSLorenzo Pieralisi struct acpi_iort_node *node, *parent; 845bc8648d4SRobin Murphy const struct iommu_ops *ops; 846643b8e4dSLorenzo Pieralisi u32 streamid = 0; 847bc8648d4SRobin Murphy int err = -ENODEV; 848643b8e4dSLorenzo Pieralisi 8494dac3210SLorenzo Pieralisi /* 8504dac3210SLorenzo Pieralisi * If we already translated the fwspec there 8514dac3210SLorenzo Pieralisi * is nothing left to do, return the iommu_ops. 8524dac3210SLorenzo Pieralisi */ 8534dac3210SLorenzo Pieralisi ops = iort_fwspec_iommu_ops(dev->iommu_fwspec); 8544dac3210SLorenzo Pieralisi if (ops) 8554dac3210SLorenzo Pieralisi return ops; 8564dac3210SLorenzo Pieralisi 857643b8e4dSLorenzo Pieralisi if (dev_is_pci(dev)) { 858643b8e4dSLorenzo Pieralisi struct pci_bus *bus = to_pci_dev(dev)->bus; 859bc8648d4SRobin Murphy struct iort_pci_alias_info info = { .dev = dev }; 860643b8e4dSLorenzo Pieralisi 861643b8e4dSLorenzo Pieralisi node = iort_scan_node(ACPI_IORT_NODE_PCI_ROOT_COMPLEX, 862643b8e4dSLorenzo Pieralisi iort_match_node_callback, &bus->dev); 863643b8e4dSLorenzo Pieralisi if (!node) 864643b8e4dSLorenzo Pieralisi return NULL; 865643b8e4dSLorenzo Pieralisi 866bc8648d4SRobin Murphy info.node = node; 867bc8648d4SRobin Murphy err = pci_for_each_dma_alias(to_pci_dev(dev), 868bc8648d4SRobin Murphy iort_pci_iommu_init, &info); 869643b8e4dSLorenzo Pieralisi } else { 870643b8e4dSLorenzo Pieralisi int i = 0; 871643b8e4dSLorenzo Pieralisi 872643b8e4dSLorenzo Pieralisi node = iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT, 873643b8e4dSLorenzo Pieralisi iort_match_node_callback, dev); 874643b8e4dSLorenzo Pieralisi if (!node) 875643b8e4dSLorenzo Pieralisi return NULL; 876643b8e4dSLorenzo Pieralisi 877bc8648d4SRobin Murphy do { 8788ca4f1d3SHanjun Guo parent = iort_node_map_platform_id(node, &streamid, 8798ca4f1d3SHanjun Guo IORT_IOMMU_TYPE, 8808ca4f1d3SHanjun Guo i++); 881bc8648d4SRobin Murphy 882bc8648d4SRobin Murphy if (parent) 883bc8648d4SRobin Murphy err = iort_iommu_xlate(dev, parent, streamid); 884bc8648d4SRobin Murphy } while (parent && !err); 885643b8e4dSLorenzo Pieralisi } 886643b8e4dSLorenzo Pieralisi 8875a1bb638SSricharan R /* 8885a1bb638SSricharan R * If we have reason to believe the IOMMU driver missed the initial 8895a1bb638SSricharan R * add_device callback for dev, replay it to get things in order. 8905a1bb638SSricharan R */ 891bc8648d4SRobin Murphy if (!err) { 8924d36037aSArnd Bergmann ops = iort_fwspec_iommu_ops(dev->iommu_fwspec); 893d49f2dedSLorenzo Pieralisi err = iort_add_device_replay(ops, dev); 894bc8648d4SRobin Murphy } 8955a1bb638SSricharan R 896058f8c3fSSricharan R /* Ignore all other errors apart from EPROBE_DEFER */ 897bc8648d4SRobin Murphy if (err == -EPROBE_DEFER) { 898bc8648d4SRobin Murphy ops = ERR_PTR(err); 899bc8648d4SRobin Murphy } else if (err) { 900bc8648d4SRobin Murphy dev_dbg(dev, "Adding to IOMMU failed: %d\n", err); 901058f8c3fSSricharan R ops = NULL; 902058f8c3fSSricharan R } 903058f8c3fSSricharan R 904643b8e4dSLorenzo Pieralisi return ops; 905643b8e4dSLorenzo Pieralisi } 906643b8e4dSLorenzo Pieralisi 907e4dadfa8SLorenzo Pieralisi static void __init acpi_iort_register_irq(int hwirq, const char *name, 908e4dadfa8SLorenzo Pieralisi int trigger, 909e4dadfa8SLorenzo Pieralisi struct resource *res) 910e4dadfa8SLorenzo Pieralisi { 911e4dadfa8SLorenzo Pieralisi int irq = acpi_register_gsi(NULL, hwirq, trigger, 912e4dadfa8SLorenzo Pieralisi ACPI_ACTIVE_HIGH); 913e4dadfa8SLorenzo Pieralisi 914e4dadfa8SLorenzo Pieralisi if (irq <= 0) { 915e4dadfa8SLorenzo Pieralisi pr_err("could not register gsi hwirq %d name [%s]\n", hwirq, 916e4dadfa8SLorenzo Pieralisi name); 917e4dadfa8SLorenzo Pieralisi return; 918e4dadfa8SLorenzo Pieralisi } 919e4dadfa8SLorenzo Pieralisi 920e4dadfa8SLorenzo Pieralisi res->start = irq; 921e4dadfa8SLorenzo Pieralisi res->end = irq; 922e4dadfa8SLorenzo Pieralisi res->flags = IORESOURCE_IRQ; 923e4dadfa8SLorenzo Pieralisi res->name = name; 924e4dadfa8SLorenzo Pieralisi } 925e4dadfa8SLorenzo Pieralisi 926e4dadfa8SLorenzo Pieralisi static int __init arm_smmu_v3_count_resources(struct acpi_iort_node *node) 927e4dadfa8SLorenzo Pieralisi { 928e4dadfa8SLorenzo Pieralisi struct acpi_iort_smmu_v3 *smmu; 929e4dadfa8SLorenzo Pieralisi /* Always present mem resource */ 930e4dadfa8SLorenzo Pieralisi int num_res = 1; 931e4dadfa8SLorenzo Pieralisi 932e4dadfa8SLorenzo Pieralisi /* Retrieve SMMUv3 specific data */ 933e4dadfa8SLorenzo Pieralisi smmu = (struct acpi_iort_smmu_v3 *)node->node_data; 934e4dadfa8SLorenzo Pieralisi 935e4dadfa8SLorenzo Pieralisi if (smmu->event_gsiv) 936e4dadfa8SLorenzo Pieralisi num_res++; 937e4dadfa8SLorenzo Pieralisi 938e4dadfa8SLorenzo Pieralisi if (smmu->pri_gsiv) 939e4dadfa8SLorenzo Pieralisi num_res++; 940e4dadfa8SLorenzo Pieralisi 941e4dadfa8SLorenzo Pieralisi if (smmu->gerr_gsiv) 942e4dadfa8SLorenzo Pieralisi num_res++; 943e4dadfa8SLorenzo Pieralisi 944e4dadfa8SLorenzo Pieralisi if (smmu->sync_gsiv) 945e4dadfa8SLorenzo Pieralisi num_res++; 946e4dadfa8SLorenzo Pieralisi 947e4dadfa8SLorenzo Pieralisi return num_res; 948e4dadfa8SLorenzo Pieralisi } 949e4dadfa8SLorenzo Pieralisi 950f935448aSGeetha Sowjanya static bool arm_smmu_v3_is_combined_irq(struct acpi_iort_smmu_v3 *smmu) 951f935448aSGeetha Sowjanya { 952f935448aSGeetha Sowjanya /* 953f935448aSGeetha Sowjanya * Cavium ThunderX2 implementation doesn't not support unique 954f935448aSGeetha Sowjanya * irq line. Use single irq line for all the SMMUv3 interrupts. 955f935448aSGeetha Sowjanya */ 956f935448aSGeetha Sowjanya if (smmu->model != ACPI_IORT_SMMU_V3_CAVIUM_CN99XX) 957f935448aSGeetha Sowjanya return false; 958f935448aSGeetha Sowjanya 959f935448aSGeetha Sowjanya /* 960f935448aSGeetha Sowjanya * ThunderX2 doesn't support MSIs from the SMMU, so we're checking 961f935448aSGeetha Sowjanya * SPI numbers here. 962f935448aSGeetha Sowjanya */ 963f935448aSGeetha Sowjanya return smmu->event_gsiv == smmu->pri_gsiv && 964f935448aSGeetha Sowjanya smmu->event_gsiv == smmu->gerr_gsiv && 965f935448aSGeetha Sowjanya smmu->event_gsiv == smmu->sync_gsiv; 966f935448aSGeetha Sowjanya } 967f935448aSGeetha Sowjanya 968403e8c7cSLinu Cherian static unsigned long arm_smmu_v3_resource_size(struct acpi_iort_smmu_v3 *smmu) 969403e8c7cSLinu Cherian { 970403e8c7cSLinu Cherian /* 971403e8c7cSLinu Cherian * Override the size, for Cavium ThunderX2 implementation 972403e8c7cSLinu Cherian * which doesn't support the page 1 SMMU register space. 973403e8c7cSLinu Cherian */ 974403e8c7cSLinu Cherian if (smmu->model == ACPI_IORT_SMMU_V3_CAVIUM_CN99XX) 975403e8c7cSLinu Cherian return SZ_64K; 976403e8c7cSLinu Cherian 977403e8c7cSLinu Cherian return SZ_128K; 978403e8c7cSLinu Cherian } 979403e8c7cSLinu Cherian 980e4dadfa8SLorenzo Pieralisi static void __init arm_smmu_v3_init_resources(struct resource *res, 981e4dadfa8SLorenzo Pieralisi struct acpi_iort_node *node) 982e4dadfa8SLorenzo Pieralisi { 983e4dadfa8SLorenzo Pieralisi struct acpi_iort_smmu_v3 *smmu; 984e4dadfa8SLorenzo Pieralisi int num_res = 0; 985e4dadfa8SLorenzo Pieralisi 986e4dadfa8SLorenzo Pieralisi /* Retrieve SMMUv3 specific data */ 987e4dadfa8SLorenzo Pieralisi smmu = (struct acpi_iort_smmu_v3 *)node->node_data; 988e4dadfa8SLorenzo Pieralisi 989e4dadfa8SLorenzo Pieralisi res[num_res].start = smmu->base_address; 990403e8c7cSLinu Cherian res[num_res].end = smmu->base_address + 991403e8c7cSLinu Cherian arm_smmu_v3_resource_size(smmu) - 1; 992e4dadfa8SLorenzo Pieralisi res[num_res].flags = IORESOURCE_MEM; 993e4dadfa8SLorenzo Pieralisi 994e4dadfa8SLorenzo Pieralisi num_res++; 995f935448aSGeetha Sowjanya if (arm_smmu_v3_is_combined_irq(smmu)) { 996f935448aSGeetha Sowjanya if (smmu->event_gsiv) 997f935448aSGeetha Sowjanya acpi_iort_register_irq(smmu->event_gsiv, "combined", 998f935448aSGeetha Sowjanya ACPI_EDGE_SENSITIVE, 999f935448aSGeetha Sowjanya &res[num_res++]); 1000f935448aSGeetha Sowjanya } else { 1001e4dadfa8SLorenzo Pieralisi 1002e4dadfa8SLorenzo Pieralisi if (smmu->event_gsiv) 1003e4dadfa8SLorenzo Pieralisi acpi_iort_register_irq(smmu->event_gsiv, "eventq", 1004e4dadfa8SLorenzo Pieralisi ACPI_EDGE_SENSITIVE, 1005e4dadfa8SLorenzo Pieralisi &res[num_res++]); 1006e4dadfa8SLorenzo Pieralisi 1007e4dadfa8SLorenzo Pieralisi if (smmu->pri_gsiv) 1008e4dadfa8SLorenzo Pieralisi acpi_iort_register_irq(smmu->pri_gsiv, "priq", 1009e4dadfa8SLorenzo Pieralisi ACPI_EDGE_SENSITIVE, 1010e4dadfa8SLorenzo Pieralisi &res[num_res++]); 1011e4dadfa8SLorenzo Pieralisi 1012e4dadfa8SLorenzo Pieralisi if (smmu->gerr_gsiv) 1013e4dadfa8SLorenzo Pieralisi acpi_iort_register_irq(smmu->gerr_gsiv, "gerror", 1014e4dadfa8SLorenzo Pieralisi ACPI_EDGE_SENSITIVE, 1015e4dadfa8SLorenzo Pieralisi &res[num_res++]); 1016e4dadfa8SLorenzo Pieralisi 1017e4dadfa8SLorenzo Pieralisi if (smmu->sync_gsiv) 1018e4dadfa8SLorenzo Pieralisi acpi_iort_register_irq(smmu->sync_gsiv, "cmdq-sync", 1019e4dadfa8SLorenzo Pieralisi ACPI_EDGE_SENSITIVE, 1020e4dadfa8SLorenzo Pieralisi &res[num_res++]); 1021e4dadfa8SLorenzo Pieralisi } 1022f935448aSGeetha Sowjanya } 1023e4dadfa8SLorenzo Pieralisi 1024e4dadfa8SLorenzo Pieralisi static bool __init arm_smmu_v3_is_coherent(struct acpi_iort_node *node) 1025e4dadfa8SLorenzo Pieralisi { 1026e4dadfa8SLorenzo Pieralisi struct acpi_iort_smmu_v3 *smmu; 1027e4dadfa8SLorenzo Pieralisi 1028e4dadfa8SLorenzo Pieralisi /* Retrieve SMMUv3 specific data */ 1029e4dadfa8SLorenzo Pieralisi smmu = (struct acpi_iort_smmu_v3 *)node->node_data; 1030e4dadfa8SLorenzo Pieralisi 1031e4dadfa8SLorenzo Pieralisi return smmu->flags & ACPI_IORT_SMMU_V3_COHACC_OVERRIDE; 1032e4dadfa8SLorenzo Pieralisi } 1033e4dadfa8SLorenzo Pieralisi 103475808131SLorenzo Pieralisi #if defined(CONFIG_ACPI_NUMA) 10355fe0ce3bSGanapatrao Kulkarni /* 10365fe0ce3bSGanapatrao Kulkarni * set numa proximity domain for smmuv3 device 10375fe0ce3bSGanapatrao Kulkarni */ 10385fe0ce3bSGanapatrao Kulkarni static void __init arm_smmu_v3_set_proximity(struct device *dev, 10395fe0ce3bSGanapatrao Kulkarni struct acpi_iort_node *node) 10405fe0ce3bSGanapatrao Kulkarni { 10415fe0ce3bSGanapatrao Kulkarni struct acpi_iort_smmu_v3 *smmu; 10425fe0ce3bSGanapatrao Kulkarni 10435fe0ce3bSGanapatrao Kulkarni smmu = (struct acpi_iort_smmu_v3 *)node->node_data; 10445fe0ce3bSGanapatrao Kulkarni if (smmu->flags & ACPI_IORT_SMMU_V3_PXM_VALID) { 10455fe0ce3bSGanapatrao Kulkarni set_dev_node(dev, acpi_map_pxm_to_node(smmu->pxm)); 10465fe0ce3bSGanapatrao Kulkarni pr_info("SMMU-v3[%llx] Mapped to Proximity domain %d\n", 10475fe0ce3bSGanapatrao Kulkarni smmu->base_address, 10485fe0ce3bSGanapatrao Kulkarni smmu->pxm); 10495fe0ce3bSGanapatrao Kulkarni } 10505fe0ce3bSGanapatrao Kulkarni } 10515fe0ce3bSGanapatrao Kulkarni #else 10525fe0ce3bSGanapatrao Kulkarni #define arm_smmu_v3_set_proximity NULL 10535fe0ce3bSGanapatrao Kulkarni #endif 10545fe0ce3bSGanapatrao Kulkarni 1055d6fcd3b1SLorenzo Pieralisi static int __init arm_smmu_count_resources(struct acpi_iort_node *node) 1056d6fcd3b1SLorenzo Pieralisi { 1057d6fcd3b1SLorenzo Pieralisi struct acpi_iort_smmu *smmu; 1058d6fcd3b1SLorenzo Pieralisi 1059d6fcd3b1SLorenzo Pieralisi /* Retrieve SMMU specific data */ 1060d6fcd3b1SLorenzo Pieralisi smmu = (struct acpi_iort_smmu *)node->node_data; 1061d6fcd3b1SLorenzo Pieralisi 1062d6fcd3b1SLorenzo Pieralisi /* 1063d6fcd3b1SLorenzo Pieralisi * Only consider the global fault interrupt and ignore the 1064d6fcd3b1SLorenzo Pieralisi * configuration access interrupt. 1065d6fcd3b1SLorenzo Pieralisi * 1066d6fcd3b1SLorenzo Pieralisi * MMIO address and global fault interrupt resources are always 1067d6fcd3b1SLorenzo Pieralisi * present so add them to the context interrupt count as a static 1068d6fcd3b1SLorenzo Pieralisi * value. 1069d6fcd3b1SLorenzo Pieralisi */ 1070d6fcd3b1SLorenzo Pieralisi return smmu->context_interrupt_count + 2; 1071d6fcd3b1SLorenzo Pieralisi } 1072d6fcd3b1SLorenzo Pieralisi 1073d6fcd3b1SLorenzo Pieralisi static void __init arm_smmu_init_resources(struct resource *res, 1074d6fcd3b1SLorenzo Pieralisi struct acpi_iort_node *node) 1075d6fcd3b1SLorenzo Pieralisi { 1076d6fcd3b1SLorenzo Pieralisi struct acpi_iort_smmu *smmu; 1077d6fcd3b1SLorenzo Pieralisi int i, hw_irq, trigger, num_res = 0; 1078d6fcd3b1SLorenzo Pieralisi u64 *ctx_irq, *glb_irq; 1079d6fcd3b1SLorenzo Pieralisi 1080d6fcd3b1SLorenzo Pieralisi /* Retrieve SMMU specific data */ 1081d6fcd3b1SLorenzo Pieralisi smmu = (struct acpi_iort_smmu *)node->node_data; 1082d6fcd3b1SLorenzo Pieralisi 1083d6fcd3b1SLorenzo Pieralisi res[num_res].start = smmu->base_address; 1084d6fcd3b1SLorenzo Pieralisi res[num_res].end = smmu->base_address + smmu->span - 1; 1085d6fcd3b1SLorenzo Pieralisi res[num_res].flags = IORESOURCE_MEM; 1086d6fcd3b1SLorenzo Pieralisi num_res++; 1087d6fcd3b1SLorenzo Pieralisi 1088d6fcd3b1SLorenzo Pieralisi glb_irq = ACPI_ADD_PTR(u64, node, smmu->global_interrupt_offset); 1089d6fcd3b1SLorenzo Pieralisi /* Global IRQs */ 1090d6fcd3b1SLorenzo Pieralisi hw_irq = IORT_IRQ_MASK(glb_irq[0]); 1091d6fcd3b1SLorenzo Pieralisi trigger = IORT_IRQ_TRIGGER_MASK(glb_irq[0]); 1092d6fcd3b1SLorenzo Pieralisi 1093d6fcd3b1SLorenzo Pieralisi acpi_iort_register_irq(hw_irq, "arm-smmu-global", trigger, 1094d6fcd3b1SLorenzo Pieralisi &res[num_res++]); 1095d6fcd3b1SLorenzo Pieralisi 1096d6fcd3b1SLorenzo Pieralisi /* Context IRQs */ 1097d6fcd3b1SLorenzo Pieralisi ctx_irq = ACPI_ADD_PTR(u64, node, smmu->context_interrupt_offset); 1098d6fcd3b1SLorenzo Pieralisi for (i = 0; i < smmu->context_interrupt_count; i++) { 1099d6fcd3b1SLorenzo Pieralisi hw_irq = IORT_IRQ_MASK(ctx_irq[i]); 1100d6fcd3b1SLorenzo Pieralisi trigger = IORT_IRQ_TRIGGER_MASK(ctx_irq[i]); 1101d6fcd3b1SLorenzo Pieralisi 1102d6fcd3b1SLorenzo Pieralisi acpi_iort_register_irq(hw_irq, "arm-smmu-context", trigger, 1103d6fcd3b1SLorenzo Pieralisi &res[num_res++]); 1104d6fcd3b1SLorenzo Pieralisi } 1105d6fcd3b1SLorenzo Pieralisi } 1106d6fcd3b1SLorenzo Pieralisi 1107d6fcd3b1SLorenzo Pieralisi static bool __init arm_smmu_is_coherent(struct acpi_iort_node *node) 1108d6fcd3b1SLorenzo Pieralisi { 1109d6fcd3b1SLorenzo Pieralisi struct acpi_iort_smmu *smmu; 1110d6fcd3b1SLorenzo Pieralisi 1111d6fcd3b1SLorenzo Pieralisi /* Retrieve SMMU specific data */ 1112d6fcd3b1SLorenzo Pieralisi smmu = (struct acpi_iort_smmu *)node->node_data; 1113d6fcd3b1SLorenzo Pieralisi 1114d6fcd3b1SLorenzo Pieralisi return smmu->flags & ACPI_IORT_SMMU_COHERENT_WALK; 1115d6fcd3b1SLorenzo Pieralisi } 1116d6fcd3b1SLorenzo Pieralisi 1117896dd2c3SLorenzo Pieralisi struct iort_dev_config { 1118846f0e9eSLorenzo Pieralisi const char *name; 1119896dd2c3SLorenzo Pieralisi int (*dev_init)(struct acpi_iort_node *node); 1120896dd2c3SLorenzo Pieralisi bool (*dev_is_coherent)(struct acpi_iort_node *node); 1121896dd2c3SLorenzo Pieralisi int (*dev_count_resources)(struct acpi_iort_node *node); 1122896dd2c3SLorenzo Pieralisi void (*dev_init_resources)(struct resource *res, 1123846f0e9eSLorenzo Pieralisi struct acpi_iort_node *node); 1124896dd2c3SLorenzo Pieralisi void (*dev_set_proximity)(struct device *dev, 11255fe0ce3bSGanapatrao Kulkarni struct acpi_iort_node *node); 1126846f0e9eSLorenzo Pieralisi }; 1127846f0e9eSLorenzo Pieralisi 1128896dd2c3SLorenzo Pieralisi static const struct iort_dev_config iort_arm_smmu_v3_cfg __initconst = { 1129e4dadfa8SLorenzo Pieralisi .name = "arm-smmu-v3", 1130896dd2c3SLorenzo Pieralisi .dev_is_coherent = arm_smmu_v3_is_coherent, 1131896dd2c3SLorenzo Pieralisi .dev_count_resources = arm_smmu_v3_count_resources, 1132896dd2c3SLorenzo Pieralisi .dev_init_resources = arm_smmu_v3_init_resources, 1133896dd2c3SLorenzo Pieralisi .dev_set_proximity = arm_smmu_v3_set_proximity, 1134e4dadfa8SLorenzo Pieralisi }; 1135e4dadfa8SLorenzo Pieralisi 1136896dd2c3SLorenzo Pieralisi static const struct iort_dev_config iort_arm_smmu_cfg __initconst = { 1137d6fcd3b1SLorenzo Pieralisi .name = "arm-smmu", 1138896dd2c3SLorenzo Pieralisi .dev_is_coherent = arm_smmu_is_coherent, 1139896dd2c3SLorenzo Pieralisi .dev_count_resources = arm_smmu_count_resources, 1140896dd2c3SLorenzo Pieralisi .dev_init_resources = arm_smmu_init_resources 1141d6fcd3b1SLorenzo Pieralisi }; 1142d6fcd3b1SLorenzo Pieralisi 1143896dd2c3SLorenzo Pieralisi static __init const struct iort_dev_config *iort_get_dev_cfg( 1144e3d49392SLorenzo Pieralisi struct acpi_iort_node *node) 1145846f0e9eSLorenzo Pieralisi { 1146e4dadfa8SLorenzo Pieralisi switch (node->type) { 1147e4dadfa8SLorenzo Pieralisi case ACPI_IORT_NODE_SMMU_V3: 1148e4dadfa8SLorenzo Pieralisi return &iort_arm_smmu_v3_cfg; 1149d6fcd3b1SLorenzo Pieralisi case ACPI_IORT_NODE_SMMU: 1150d6fcd3b1SLorenzo Pieralisi return &iort_arm_smmu_cfg; 1151e4dadfa8SLorenzo Pieralisi default: 1152846f0e9eSLorenzo Pieralisi return NULL; 1153846f0e9eSLorenzo Pieralisi } 1154e4dadfa8SLorenzo Pieralisi } 1155846f0e9eSLorenzo Pieralisi 1156846f0e9eSLorenzo Pieralisi /** 1157896dd2c3SLorenzo Pieralisi * iort_add_platform_device() - Allocate a platform device for IORT node 1158896dd2c3SLorenzo Pieralisi * @node: Pointer to device ACPI IORT node 1159846f0e9eSLorenzo Pieralisi * 1160846f0e9eSLorenzo Pieralisi * Returns: 0 on success, <0 failure 1161846f0e9eSLorenzo Pieralisi */ 1162896dd2c3SLorenzo Pieralisi static int __init iort_add_platform_device(struct acpi_iort_node *node, 1163896dd2c3SLorenzo Pieralisi const struct iort_dev_config *ops) 1164846f0e9eSLorenzo Pieralisi { 1165846f0e9eSLorenzo Pieralisi struct fwnode_handle *fwnode; 1166846f0e9eSLorenzo Pieralisi struct platform_device *pdev; 1167846f0e9eSLorenzo Pieralisi struct resource *r; 1168846f0e9eSLorenzo Pieralisi enum dev_dma_attr attr; 1169846f0e9eSLorenzo Pieralisi int ret, count; 1170846f0e9eSLorenzo Pieralisi 1171846f0e9eSLorenzo Pieralisi pdev = platform_device_alloc(ops->name, PLATFORM_DEVID_AUTO); 1172846f0e9eSLorenzo Pieralisi if (!pdev) 11735e5afa6cSDan Carpenter return -ENOMEM; 1174846f0e9eSLorenzo Pieralisi 1175896dd2c3SLorenzo Pieralisi if (ops->dev_set_proximity) 1176896dd2c3SLorenzo Pieralisi ops->dev_set_proximity(&pdev->dev, node); 11775fe0ce3bSGanapatrao Kulkarni 1178896dd2c3SLorenzo Pieralisi count = ops->dev_count_resources(node); 1179846f0e9eSLorenzo Pieralisi 1180846f0e9eSLorenzo Pieralisi r = kcalloc(count, sizeof(*r), GFP_KERNEL); 1181846f0e9eSLorenzo Pieralisi if (!r) { 1182846f0e9eSLorenzo Pieralisi ret = -ENOMEM; 1183846f0e9eSLorenzo Pieralisi goto dev_put; 1184846f0e9eSLorenzo Pieralisi } 1185846f0e9eSLorenzo Pieralisi 1186896dd2c3SLorenzo Pieralisi ops->dev_init_resources(r, node); 1187846f0e9eSLorenzo Pieralisi 1188846f0e9eSLorenzo Pieralisi ret = platform_device_add_resources(pdev, r, count); 1189846f0e9eSLorenzo Pieralisi /* 1190846f0e9eSLorenzo Pieralisi * Resources are duplicated in platform_device_add_resources, 1191846f0e9eSLorenzo Pieralisi * free their allocated memory 1192846f0e9eSLorenzo Pieralisi */ 1193846f0e9eSLorenzo Pieralisi kfree(r); 1194846f0e9eSLorenzo Pieralisi 1195846f0e9eSLorenzo Pieralisi if (ret) 1196846f0e9eSLorenzo Pieralisi goto dev_put; 1197846f0e9eSLorenzo Pieralisi 1198846f0e9eSLorenzo Pieralisi /* 1199846f0e9eSLorenzo Pieralisi * Add a copy of IORT node pointer to platform_data to 1200846f0e9eSLorenzo Pieralisi * be used to retrieve IORT data information. 1201846f0e9eSLorenzo Pieralisi */ 1202846f0e9eSLorenzo Pieralisi ret = platform_device_add_data(pdev, &node, sizeof(node)); 1203846f0e9eSLorenzo Pieralisi if (ret) 1204846f0e9eSLorenzo Pieralisi goto dev_put; 1205846f0e9eSLorenzo Pieralisi 1206846f0e9eSLorenzo Pieralisi /* 1207846f0e9eSLorenzo Pieralisi * We expect the dma masks to be equivalent for 1208846f0e9eSLorenzo Pieralisi * all SMMUs set-ups 1209846f0e9eSLorenzo Pieralisi */ 1210846f0e9eSLorenzo Pieralisi pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask; 1211846f0e9eSLorenzo Pieralisi 1212846f0e9eSLorenzo Pieralisi fwnode = iort_get_fwnode(node); 1213846f0e9eSLorenzo Pieralisi 1214846f0e9eSLorenzo Pieralisi if (!fwnode) { 1215846f0e9eSLorenzo Pieralisi ret = -ENODEV; 1216846f0e9eSLorenzo Pieralisi goto dev_put; 1217846f0e9eSLorenzo Pieralisi } 1218846f0e9eSLorenzo Pieralisi 1219846f0e9eSLorenzo Pieralisi pdev->dev.fwnode = fwnode; 1220846f0e9eSLorenzo Pieralisi 1221896dd2c3SLorenzo Pieralisi attr = ops->dev_is_coherent && ops->dev_is_coherent(node) ? 1222846f0e9eSLorenzo Pieralisi DEV_DMA_COHERENT : DEV_DMA_NON_COHERENT; 1223846f0e9eSLorenzo Pieralisi 1224846f0e9eSLorenzo Pieralisi /* Configure DMA for the page table walker */ 1225846f0e9eSLorenzo Pieralisi acpi_dma_configure(&pdev->dev, attr); 1226846f0e9eSLorenzo Pieralisi 1227846f0e9eSLorenzo Pieralisi ret = platform_device_add(pdev); 1228846f0e9eSLorenzo Pieralisi if (ret) 1229846f0e9eSLorenzo Pieralisi goto dma_deconfigure; 1230846f0e9eSLorenzo Pieralisi 1231846f0e9eSLorenzo Pieralisi return 0; 1232846f0e9eSLorenzo Pieralisi 1233846f0e9eSLorenzo Pieralisi dma_deconfigure: 1234846f0e9eSLorenzo Pieralisi acpi_dma_deconfigure(&pdev->dev); 1235846f0e9eSLorenzo Pieralisi dev_put: 1236846f0e9eSLorenzo Pieralisi platform_device_put(pdev); 1237846f0e9eSLorenzo Pieralisi 1238846f0e9eSLorenzo Pieralisi return ret; 1239846f0e9eSLorenzo Pieralisi } 1240846f0e9eSLorenzo Pieralisi 1241846f0e9eSLorenzo Pieralisi static void __init iort_init_platform_devices(void) 1242846f0e9eSLorenzo Pieralisi { 1243846f0e9eSLorenzo Pieralisi struct acpi_iort_node *iort_node, *iort_end; 1244846f0e9eSLorenzo Pieralisi struct acpi_table_iort *iort; 1245846f0e9eSLorenzo Pieralisi struct fwnode_handle *fwnode; 1246846f0e9eSLorenzo Pieralisi int i, ret; 1247896dd2c3SLorenzo Pieralisi const struct iort_dev_config *ops; 1248846f0e9eSLorenzo Pieralisi 1249846f0e9eSLorenzo Pieralisi /* 1250846f0e9eSLorenzo Pieralisi * iort_table and iort both point to the start of IORT table, but 1251846f0e9eSLorenzo Pieralisi * have different struct types 1252846f0e9eSLorenzo Pieralisi */ 1253846f0e9eSLorenzo Pieralisi iort = (struct acpi_table_iort *)iort_table; 1254846f0e9eSLorenzo Pieralisi 1255846f0e9eSLorenzo Pieralisi /* Get the first IORT node */ 1256846f0e9eSLorenzo Pieralisi iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort, 1257846f0e9eSLorenzo Pieralisi iort->node_offset); 1258846f0e9eSLorenzo Pieralisi iort_end = ACPI_ADD_PTR(struct acpi_iort_node, iort, 1259846f0e9eSLorenzo Pieralisi iort_table->length); 1260846f0e9eSLorenzo Pieralisi 1261846f0e9eSLorenzo Pieralisi for (i = 0; i < iort->node_count; i++) { 1262846f0e9eSLorenzo Pieralisi if (iort_node >= iort_end) { 1263846f0e9eSLorenzo Pieralisi pr_err("iort node pointer overflows, bad table\n"); 1264846f0e9eSLorenzo Pieralisi return; 1265846f0e9eSLorenzo Pieralisi } 1266846f0e9eSLorenzo Pieralisi 1267896dd2c3SLorenzo Pieralisi ops = iort_get_dev_cfg(iort_node); 1268896dd2c3SLorenzo Pieralisi if (ops) { 1269846f0e9eSLorenzo Pieralisi fwnode = acpi_alloc_fwnode_static(); 1270846f0e9eSLorenzo Pieralisi if (!fwnode) 1271846f0e9eSLorenzo Pieralisi return; 1272846f0e9eSLorenzo Pieralisi 1273846f0e9eSLorenzo Pieralisi iort_set_fwnode(iort_node, fwnode); 1274846f0e9eSLorenzo Pieralisi 1275896dd2c3SLorenzo Pieralisi ret = iort_add_platform_device(iort_node, ops); 1276846f0e9eSLorenzo Pieralisi if (ret) { 1277846f0e9eSLorenzo Pieralisi iort_delete_fwnode(iort_node); 1278846f0e9eSLorenzo Pieralisi acpi_free_fwnode_static(fwnode); 1279846f0e9eSLorenzo Pieralisi return; 1280846f0e9eSLorenzo Pieralisi } 1281846f0e9eSLorenzo Pieralisi } 1282846f0e9eSLorenzo Pieralisi 1283846f0e9eSLorenzo Pieralisi iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort_node, 1284846f0e9eSLorenzo Pieralisi iort_node->length); 1285846f0e9eSLorenzo Pieralisi } 1286846f0e9eSLorenzo Pieralisi } 1287846f0e9eSLorenzo Pieralisi 128888ef16d8STomasz Nowicki void __init acpi_iort_init(void) 128988ef16d8STomasz Nowicki { 129088ef16d8STomasz Nowicki acpi_status status; 129188ef16d8STomasz Nowicki 129288ef16d8STomasz Nowicki status = acpi_get_table(ACPI_SIG_IORT, 0, &iort_table); 129334ceea27SLorenzo Pieralisi if (ACPI_FAILURE(status)) { 129434ceea27SLorenzo Pieralisi if (status != AE_NOT_FOUND) { 129588ef16d8STomasz Nowicki const char *msg = acpi_format_exception(status); 129634ceea27SLorenzo Pieralisi 129788ef16d8STomasz Nowicki pr_err("Failed to get table, %s\n", msg); 129888ef16d8STomasz Nowicki } 129934ceea27SLorenzo Pieralisi 130034ceea27SLorenzo Pieralisi return; 130134ceea27SLorenzo Pieralisi } 130234ceea27SLorenzo Pieralisi 1303846f0e9eSLorenzo Pieralisi iort_init_platform_devices(); 130488ef16d8STomasz Nowicki } 1305