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 || 359*86456a3fSHanjun Guo node->type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX || 360*86456a3fSHanjun Guo node->type == ACPI_IORT_NODE_SMMU_V3) { 361030abd8aSLorenzo Pieralisi *id_out = map->output_base; 362618f535aSLorenzo Pieralisi return parent; 363618f535aSLorenzo Pieralisi } 364618f535aSLorenzo Pieralisi } 365618f535aSLorenzo Pieralisi 366618f535aSLorenzo Pieralisi return NULL; 367618f535aSLorenzo Pieralisi } 368618f535aSLorenzo Pieralisi 369*86456a3fSHanjun Guo #if (ACPI_CA_VERSION > 0x20170929) 370*86456a3fSHanjun Guo static int iort_get_id_mapping_index(struct acpi_iort_node *node) 371*86456a3fSHanjun Guo { 372*86456a3fSHanjun Guo struct acpi_iort_smmu_v3 *smmu; 373*86456a3fSHanjun Guo 374*86456a3fSHanjun Guo switch (node->type) { 375*86456a3fSHanjun Guo case ACPI_IORT_NODE_SMMU_V3: 376*86456a3fSHanjun Guo /* 377*86456a3fSHanjun Guo * SMMUv3 dev ID mapping index was introduced in revision 1 378*86456a3fSHanjun Guo * table, not available in revision 0 379*86456a3fSHanjun Guo */ 380*86456a3fSHanjun Guo if (node->revision < 1) 381*86456a3fSHanjun Guo return -EINVAL; 382*86456a3fSHanjun Guo 383*86456a3fSHanjun Guo smmu = (struct acpi_iort_smmu_v3 *)node->node_data; 384*86456a3fSHanjun Guo /* 385*86456a3fSHanjun Guo * ID mapping index is only ignored if all interrupts are 386*86456a3fSHanjun Guo * GSIV based 387*86456a3fSHanjun Guo */ 388*86456a3fSHanjun Guo if (smmu->event_gsiv && smmu->pri_gsiv && smmu->gerr_gsiv 389*86456a3fSHanjun Guo && smmu->sync_gsiv) 390*86456a3fSHanjun Guo return -EINVAL; 391*86456a3fSHanjun Guo 392*86456a3fSHanjun Guo if (smmu->id_mapping_index >= node->mapping_count) { 393*86456a3fSHanjun Guo pr_err(FW_BUG "[node %p type %d] ID mapping index overflows valid mappings\n", 394*86456a3fSHanjun Guo node, node->type); 395*86456a3fSHanjun Guo return -EINVAL; 396*86456a3fSHanjun Guo } 397*86456a3fSHanjun Guo 398*86456a3fSHanjun Guo return smmu->id_mapping_index; 399*86456a3fSHanjun Guo default: 400*86456a3fSHanjun Guo return -EINVAL; 401*86456a3fSHanjun Guo } 402*86456a3fSHanjun Guo } 403*86456a3fSHanjun Guo #else 4048c8df8dcSHanjun Guo static inline int iort_get_id_mapping_index(struct acpi_iort_node *node) 4058c8df8dcSHanjun Guo { 4068c8df8dcSHanjun Guo return -EINVAL; 4078c8df8dcSHanjun Guo } 408*86456a3fSHanjun Guo #endif 4098c8df8dcSHanjun Guo 410697f6093SHanjun Guo static struct acpi_iort_node *iort_node_map_id(struct acpi_iort_node *node, 411697f6093SHanjun Guo u32 id_in, u32 *id_out, 412ea50b524SLorenzo Pieralisi u8 type_mask) 41388ef16d8STomasz Nowicki { 414697f6093SHanjun Guo u32 id = id_in; 41588ef16d8STomasz Nowicki 41688ef16d8STomasz Nowicki /* Parse the ID mapping tree to find specified node type */ 41788ef16d8STomasz Nowicki while (node) { 41888ef16d8STomasz Nowicki struct acpi_iort_id_mapping *map; 4198c8df8dcSHanjun Guo int i, index; 42088ef16d8STomasz Nowicki 421ea50b524SLorenzo Pieralisi if (IORT_TYPE_MASK(node->type) & type_mask) { 422697f6093SHanjun Guo if (id_out) 423697f6093SHanjun Guo *id_out = id; 42488ef16d8STomasz Nowicki return node; 42588ef16d8STomasz Nowicki } 42688ef16d8STomasz Nowicki 42788ef16d8STomasz Nowicki if (!node->mapping_offset || !node->mapping_count) 42888ef16d8STomasz Nowicki goto fail_map; 42988ef16d8STomasz Nowicki 43088ef16d8STomasz Nowicki map = ACPI_ADD_PTR(struct acpi_iort_id_mapping, node, 43188ef16d8STomasz Nowicki node->mapping_offset); 43288ef16d8STomasz Nowicki 43388ef16d8STomasz Nowicki /* Firmware bug! */ 43488ef16d8STomasz Nowicki if (!map->output_reference) { 43588ef16d8STomasz Nowicki pr_err(FW_BUG "[node %p type %d] ID map has NULL parent reference\n", 43688ef16d8STomasz Nowicki node, node->type); 43788ef16d8STomasz Nowicki goto fail_map; 43888ef16d8STomasz Nowicki } 43988ef16d8STomasz Nowicki 4408c8df8dcSHanjun Guo /* 4418c8df8dcSHanjun Guo * Get the special ID mapping index (if any) and skip its 4428c8df8dcSHanjun Guo * associated ID map to prevent erroneous multi-stage 4438c8df8dcSHanjun Guo * IORT ID translations. 4448c8df8dcSHanjun Guo */ 4458c8df8dcSHanjun Guo index = iort_get_id_mapping_index(node); 4468c8df8dcSHanjun Guo 447697f6093SHanjun Guo /* Do the ID translation */ 44888ef16d8STomasz Nowicki for (i = 0; i < node->mapping_count; i++, map++) { 4498c8df8dcSHanjun Guo /* if it is special mapping index, skip it */ 4508c8df8dcSHanjun Guo if (i == index) 4518c8df8dcSHanjun Guo continue; 4528c8df8dcSHanjun Guo 453697f6093SHanjun Guo if (!iort_id_map(map, node->type, id, &id)) 45488ef16d8STomasz Nowicki break; 45588ef16d8STomasz Nowicki } 45688ef16d8STomasz Nowicki 45788ef16d8STomasz Nowicki if (i == node->mapping_count) 45888ef16d8STomasz Nowicki goto fail_map; 45988ef16d8STomasz Nowicki 46088ef16d8STomasz Nowicki node = ACPI_ADD_PTR(struct acpi_iort_node, iort_table, 46188ef16d8STomasz Nowicki map->output_reference); 46288ef16d8STomasz Nowicki } 46388ef16d8STomasz Nowicki 46488ef16d8STomasz Nowicki fail_map: 465697f6093SHanjun Guo /* Map input ID to output ID unchanged on mapping failure */ 466697f6093SHanjun Guo if (id_out) 467697f6093SHanjun Guo *id_out = id_in; 46888ef16d8STomasz Nowicki 46988ef16d8STomasz Nowicki return NULL; 47088ef16d8STomasz Nowicki } 47188ef16d8STomasz Nowicki 472e3d49392SLorenzo Pieralisi static struct acpi_iort_node *iort_node_map_platform_id( 473e3d49392SLorenzo Pieralisi struct acpi_iort_node *node, u32 *id_out, u8 type_mask, 4748ca4f1d3SHanjun Guo int index) 4758ca4f1d3SHanjun Guo { 4768ca4f1d3SHanjun Guo struct acpi_iort_node *parent; 4778ca4f1d3SHanjun Guo u32 id; 4788ca4f1d3SHanjun Guo 4798ca4f1d3SHanjun Guo /* step 1: retrieve the initial dev id */ 4808ca4f1d3SHanjun Guo parent = iort_node_get_id(node, &id, index); 4818ca4f1d3SHanjun Guo if (!parent) 4828ca4f1d3SHanjun Guo return NULL; 4838ca4f1d3SHanjun Guo 4848ca4f1d3SHanjun Guo /* 4858ca4f1d3SHanjun Guo * optional step 2: map the initial dev id if its parent is not 4868ca4f1d3SHanjun Guo * the target type we want, map it again for the use cases such 4878ca4f1d3SHanjun Guo * as NC (named component) -> SMMU -> ITS. If the type is matched, 4888ca4f1d3SHanjun Guo * return the initial dev id and its parent pointer directly. 4898ca4f1d3SHanjun Guo */ 4908ca4f1d3SHanjun Guo if (!(IORT_TYPE_MASK(parent->type) & type_mask)) 4918ca4f1d3SHanjun Guo parent = iort_node_map_id(parent, id, id_out, type_mask); 4928ca4f1d3SHanjun Guo else 4938ca4f1d3SHanjun Guo if (id_out) 4948ca4f1d3SHanjun Guo *id_out = id; 4958ca4f1d3SHanjun Guo 4968ca4f1d3SHanjun Guo return parent; 4978ca4f1d3SHanjun Guo } 4988ca4f1d3SHanjun Guo 49988ef16d8STomasz Nowicki static struct acpi_iort_node *iort_find_dev_node(struct device *dev) 50088ef16d8STomasz Nowicki { 50188ef16d8STomasz Nowicki struct pci_bus *pbus; 50288ef16d8STomasz Nowicki 5030a71d8b9SHanjun Guo if (!dev_is_pci(dev)) { 5040a71d8b9SHanjun Guo struct acpi_iort_node *node; 5050a71d8b9SHanjun Guo /* 5060a71d8b9SHanjun Guo * scan iort_fwnode_list to see if it's an iort platform 5070a71d8b9SHanjun Guo * device (such as SMMU, PMCG),its iort node already cached 5080a71d8b9SHanjun Guo * and associated with fwnode when iort platform devices 5090a71d8b9SHanjun Guo * were initialized. 5100a71d8b9SHanjun Guo */ 5110a71d8b9SHanjun Guo node = iort_get_iort_node(dev->fwnode); 5120a71d8b9SHanjun Guo if (node) 5130a71d8b9SHanjun Guo return node; 5140a71d8b9SHanjun Guo 5150a71d8b9SHanjun Guo /* 5160a71d8b9SHanjun Guo * if not, then it should be a platform device defined in 5170a71d8b9SHanjun Guo * DSDT/SSDT (with Named Component node in IORT) 5180a71d8b9SHanjun Guo */ 51988ef16d8STomasz Nowicki return iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT, 52088ef16d8STomasz Nowicki iort_match_node_callback, dev); 5210a71d8b9SHanjun Guo } 52288ef16d8STomasz Nowicki 52388ef16d8STomasz Nowicki /* Find a PCI root bus */ 52488ef16d8STomasz Nowicki pbus = to_pci_dev(dev)->bus; 52588ef16d8STomasz Nowicki while (!pci_is_root_bus(pbus)) 52688ef16d8STomasz Nowicki pbus = pbus->parent; 52788ef16d8STomasz Nowicki 52888ef16d8STomasz Nowicki return iort_scan_node(ACPI_IORT_NODE_PCI_ROOT_COMPLEX, 52988ef16d8STomasz Nowicki iort_match_node_callback, &pbus->dev); 53088ef16d8STomasz Nowicki } 53188ef16d8STomasz Nowicki 5324bf2efd2STomasz Nowicki /** 5334bf2efd2STomasz Nowicki * iort_msi_map_rid() - Map a MSI requester ID for a device 5344bf2efd2STomasz Nowicki * @dev: The device for which the mapping is to be done. 5354bf2efd2STomasz Nowicki * @req_id: The device requester ID. 5364bf2efd2STomasz Nowicki * 5374bf2efd2STomasz Nowicki * Returns: mapped MSI RID on success, input requester ID otherwise 5384bf2efd2STomasz Nowicki */ 5394bf2efd2STomasz Nowicki u32 iort_msi_map_rid(struct device *dev, u32 req_id) 5404bf2efd2STomasz Nowicki { 5414bf2efd2STomasz Nowicki struct acpi_iort_node *node; 5424bf2efd2STomasz Nowicki u32 dev_id; 5434bf2efd2STomasz Nowicki 5444bf2efd2STomasz Nowicki node = iort_find_dev_node(dev); 5454bf2efd2STomasz Nowicki if (!node) 5464bf2efd2STomasz Nowicki return req_id; 5474bf2efd2STomasz Nowicki 548697f6093SHanjun Guo iort_node_map_id(node, req_id, &dev_id, IORT_MSI_TYPE); 5494bf2efd2STomasz Nowicki return dev_id; 5504bf2efd2STomasz Nowicki } 5514bf2efd2STomasz Nowicki 5524bf2efd2STomasz Nowicki /** 553ae7c1838SHanjun Guo * iort_pmsi_get_dev_id() - Get the device id for a device 554ae7c1838SHanjun Guo * @dev: The device for which the mapping is to be done. 555ae7c1838SHanjun Guo * @dev_id: The device ID found. 556ae7c1838SHanjun Guo * 557ae7c1838SHanjun Guo * Returns: 0 for successful find a dev id, -ENODEV on error 558ae7c1838SHanjun Guo */ 559ae7c1838SHanjun Guo int iort_pmsi_get_dev_id(struct device *dev, u32 *dev_id) 560ae7c1838SHanjun Guo { 5618c8df8dcSHanjun Guo int i, index; 562ae7c1838SHanjun Guo struct acpi_iort_node *node; 563ae7c1838SHanjun Guo 564ae7c1838SHanjun Guo node = iort_find_dev_node(dev); 565ae7c1838SHanjun Guo if (!node) 566ae7c1838SHanjun Guo return -ENODEV; 567ae7c1838SHanjun Guo 5688c8df8dcSHanjun Guo index = iort_get_id_mapping_index(node); 5698c8df8dcSHanjun Guo /* if there is a valid index, go get the dev_id directly */ 5708c8df8dcSHanjun Guo if (index >= 0) { 5718c8df8dcSHanjun Guo if (iort_node_get_id(node, dev_id, index)) 572ae7c1838SHanjun Guo return 0; 5738c8df8dcSHanjun Guo } else { 5748c8df8dcSHanjun Guo for (i = 0; i < node->mapping_count; i++) { 5758c8df8dcSHanjun Guo if (iort_node_map_platform_id(node, dev_id, 5768c8df8dcSHanjun Guo IORT_MSI_TYPE, i)) 5778c8df8dcSHanjun Guo return 0; 5788c8df8dcSHanjun Guo } 579ae7c1838SHanjun Guo } 580ae7c1838SHanjun Guo 581ae7c1838SHanjun Guo return -ENODEV; 582ae7c1838SHanjun Guo } 583ae7c1838SHanjun Guo 584ae7c1838SHanjun Guo /** 5854bf2efd2STomasz Nowicki * iort_dev_find_its_id() - Find the ITS identifier for a device 5864bf2efd2STomasz Nowicki * @dev: The device. 5876cb6bf56SHanjun Guo * @req_id: Device's requester ID 5884bf2efd2STomasz Nowicki * @idx: Index of the ITS identifier list. 5894bf2efd2STomasz Nowicki * @its_id: ITS identifier. 5904bf2efd2STomasz Nowicki * 5914bf2efd2STomasz Nowicki * Returns: 0 on success, appropriate error value otherwise 5924bf2efd2STomasz Nowicki */ 5934bf2efd2STomasz Nowicki static int iort_dev_find_its_id(struct device *dev, u32 req_id, 5944bf2efd2STomasz Nowicki unsigned int idx, int *its_id) 5954bf2efd2STomasz Nowicki { 5964bf2efd2STomasz Nowicki struct acpi_iort_its_group *its; 5974bf2efd2STomasz Nowicki struct acpi_iort_node *node; 5984bf2efd2STomasz Nowicki 5994bf2efd2STomasz Nowicki node = iort_find_dev_node(dev); 6004bf2efd2STomasz Nowicki if (!node) 6014bf2efd2STomasz Nowicki return -ENXIO; 6024bf2efd2STomasz Nowicki 603697f6093SHanjun Guo node = iort_node_map_id(node, req_id, NULL, IORT_MSI_TYPE); 6044bf2efd2STomasz Nowicki if (!node) 6054bf2efd2STomasz Nowicki return -ENXIO; 6064bf2efd2STomasz Nowicki 6074bf2efd2STomasz Nowicki /* Move to ITS specific data */ 6084bf2efd2STomasz Nowicki its = (struct acpi_iort_its_group *)node->node_data; 6094bf2efd2STomasz Nowicki if (idx > its->its_count) { 6104bf2efd2STomasz Nowicki dev_err(dev, "requested ITS ID index [%d] is greater than available [%d]\n", 6114bf2efd2STomasz Nowicki idx, its->its_count); 6124bf2efd2STomasz Nowicki return -ENXIO; 6134bf2efd2STomasz Nowicki } 6144bf2efd2STomasz Nowicki 6154bf2efd2STomasz Nowicki *its_id = its->identifiers[idx]; 6164bf2efd2STomasz Nowicki return 0; 6174bf2efd2STomasz Nowicki } 6184bf2efd2STomasz Nowicki 6194bf2efd2STomasz Nowicki /** 6204bf2efd2STomasz Nowicki * iort_get_device_domain() - Find MSI domain related to a device 6214bf2efd2STomasz Nowicki * @dev: The device. 6224bf2efd2STomasz Nowicki * @req_id: Requester ID for the device. 6234bf2efd2STomasz Nowicki * 6244bf2efd2STomasz Nowicki * Returns: the MSI domain for this device, NULL otherwise 6254bf2efd2STomasz Nowicki */ 6264bf2efd2STomasz Nowicki struct irq_domain *iort_get_device_domain(struct device *dev, u32 req_id) 6274bf2efd2STomasz Nowicki { 6284bf2efd2STomasz Nowicki struct fwnode_handle *handle; 6294bf2efd2STomasz Nowicki int its_id; 6304bf2efd2STomasz Nowicki 6314bf2efd2STomasz Nowicki if (iort_dev_find_its_id(dev, req_id, 0, &its_id)) 6324bf2efd2STomasz Nowicki return NULL; 6334bf2efd2STomasz Nowicki 6344bf2efd2STomasz Nowicki handle = iort_find_domain_token(its_id); 6354bf2efd2STomasz Nowicki if (!handle) 6364bf2efd2STomasz Nowicki return NULL; 6374bf2efd2STomasz Nowicki 6384bf2efd2STomasz Nowicki return irq_find_matching_fwnode(handle, DOMAIN_BUS_PCI_MSI); 6394bf2efd2STomasz Nowicki } 6404bf2efd2STomasz Nowicki 641d4f54a18SHanjun Guo /** 642d4f54a18SHanjun Guo * iort_get_platform_device_domain() - Find MSI domain related to a 643d4f54a18SHanjun Guo * platform device 644d4f54a18SHanjun Guo * @dev: the dev pointer associated with the platform device 645d4f54a18SHanjun Guo * 646d4f54a18SHanjun Guo * Returns: the MSI domain for this device, NULL otherwise 647d4f54a18SHanjun Guo */ 648d4f54a18SHanjun Guo static struct irq_domain *iort_get_platform_device_domain(struct device *dev) 649d4f54a18SHanjun Guo { 650d4f54a18SHanjun Guo struct acpi_iort_node *node, *msi_parent; 651d4f54a18SHanjun Guo struct fwnode_handle *iort_fwnode; 652d4f54a18SHanjun Guo struct acpi_iort_its_group *its; 653d4f54a18SHanjun Guo int i; 654d4f54a18SHanjun Guo 655d4f54a18SHanjun Guo /* find its associated iort node */ 656d4f54a18SHanjun Guo node = iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT, 657d4f54a18SHanjun Guo iort_match_node_callback, dev); 658d4f54a18SHanjun Guo if (!node) 659d4f54a18SHanjun Guo return NULL; 660d4f54a18SHanjun Guo 661d4f54a18SHanjun Guo /* then find its msi parent node */ 662d4f54a18SHanjun Guo for (i = 0; i < node->mapping_count; i++) { 663d4f54a18SHanjun Guo msi_parent = iort_node_map_platform_id(node, NULL, 664d4f54a18SHanjun Guo IORT_MSI_TYPE, i); 665d4f54a18SHanjun Guo if (msi_parent) 666d4f54a18SHanjun Guo break; 667d4f54a18SHanjun Guo } 668d4f54a18SHanjun Guo 669d4f54a18SHanjun Guo if (!msi_parent) 670d4f54a18SHanjun Guo return NULL; 671d4f54a18SHanjun Guo 672d4f54a18SHanjun Guo /* Move to ITS specific data */ 673d4f54a18SHanjun Guo its = (struct acpi_iort_its_group *)msi_parent->node_data; 674d4f54a18SHanjun Guo 675d4f54a18SHanjun Guo iort_fwnode = iort_find_domain_token(its->identifiers[0]); 676d4f54a18SHanjun Guo if (!iort_fwnode) 677d4f54a18SHanjun Guo return NULL; 678d4f54a18SHanjun Guo 679d4f54a18SHanjun Guo return irq_find_matching_fwnode(iort_fwnode, DOMAIN_BUS_PLATFORM_MSI); 680d4f54a18SHanjun Guo } 681d4f54a18SHanjun Guo 682d4f54a18SHanjun Guo void acpi_configure_pmsi_domain(struct device *dev) 683d4f54a18SHanjun Guo { 684d4f54a18SHanjun Guo struct irq_domain *msi_domain; 685d4f54a18SHanjun Guo 686d4f54a18SHanjun Guo msi_domain = iort_get_platform_device_domain(dev); 687d4f54a18SHanjun Guo if (msi_domain) 688d4f54a18SHanjun Guo dev_set_msi_domain(dev, msi_domain); 689d4f54a18SHanjun Guo } 690d4f54a18SHanjun Guo 691bc8648d4SRobin Murphy static int __maybe_unused __get_pci_rid(struct pci_dev *pdev, u16 alias, 692bc8648d4SRobin Murphy void *data) 693643b8e4dSLorenzo Pieralisi { 694643b8e4dSLorenzo Pieralisi u32 *rid = data; 695643b8e4dSLorenzo Pieralisi 696643b8e4dSLorenzo Pieralisi *rid = alias; 697643b8e4dSLorenzo Pieralisi return 0; 698643b8e4dSLorenzo Pieralisi } 699643b8e4dSLorenzo Pieralisi 700643b8e4dSLorenzo Pieralisi static int arm_smmu_iort_xlate(struct device *dev, u32 streamid, 701643b8e4dSLorenzo Pieralisi struct fwnode_handle *fwnode, 702643b8e4dSLorenzo Pieralisi const struct iommu_ops *ops) 703643b8e4dSLorenzo Pieralisi { 704643b8e4dSLorenzo Pieralisi int ret = iommu_fwspec_init(dev, fwnode, ops); 705643b8e4dSLorenzo Pieralisi 706643b8e4dSLorenzo Pieralisi if (!ret) 707643b8e4dSLorenzo Pieralisi ret = iommu_fwspec_add_ids(dev, &streamid, 1); 708643b8e4dSLorenzo Pieralisi 709643b8e4dSLorenzo Pieralisi return ret; 710643b8e4dSLorenzo Pieralisi } 711643b8e4dSLorenzo Pieralisi 7121d9029d4SLorenzo Pieralisi static inline bool iort_iommu_driver_enabled(u8 type) 7131d9029d4SLorenzo Pieralisi { 7141d9029d4SLorenzo Pieralisi switch (type) { 7151d9029d4SLorenzo Pieralisi case ACPI_IORT_NODE_SMMU_V3: 7161d9029d4SLorenzo Pieralisi return IS_BUILTIN(CONFIG_ARM_SMMU_V3); 7171d9029d4SLorenzo Pieralisi case ACPI_IORT_NODE_SMMU: 7181d9029d4SLorenzo Pieralisi return IS_BUILTIN(CONFIG_ARM_SMMU); 7191d9029d4SLorenzo Pieralisi default: 7201d9029d4SLorenzo Pieralisi pr_warn("IORT node type %u does not describe an SMMU\n", type); 7211d9029d4SLorenzo Pieralisi return false; 7221d9029d4SLorenzo Pieralisi } 7231d9029d4SLorenzo Pieralisi } 7241d9029d4SLorenzo Pieralisi 725d49f2dedSLorenzo Pieralisi #ifdef CONFIG_IOMMU_API 726e3d49392SLorenzo Pieralisi static inline const struct iommu_ops *iort_fwspec_iommu_ops( 727e3d49392SLorenzo Pieralisi struct iommu_fwspec *fwspec) 728d49f2dedSLorenzo Pieralisi { 729d49f2dedSLorenzo Pieralisi return (fwspec && fwspec->ops) ? fwspec->ops : NULL; 730d49f2dedSLorenzo Pieralisi } 731d49f2dedSLorenzo Pieralisi 732e3d49392SLorenzo Pieralisi static inline int iort_add_device_replay(const struct iommu_ops *ops, 733e3d49392SLorenzo Pieralisi struct device *dev) 734d49f2dedSLorenzo Pieralisi { 735d49f2dedSLorenzo Pieralisi int err = 0; 736d49f2dedSLorenzo Pieralisi 737bc8648d4SRobin Murphy if (ops->add_device && dev->bus && !dev->iommu_group) 738d49f2dedSLorenzo Pieralisi err = ops->add_device(dev); 739d49f2dedSLorenzo Pieralisi 740d49f2dedSLorenzo Pieralisi return err; 741d49f2dedSLorenzo Pieralisi } 742d49f2dedSLorenzo Pieralisi #else 743e3d49392SLorenzo Pieralisi static inline const struct iommu_ops *iort_fwspec_iommu_ops( 744e3d49392SLorenzo Pieralisi struct iommu_fwspec *fwspec) 745d49f2dedSLorenzo Pieralisi { return NULL; } 746e3d49392SLorenzo Pieralisi static inline int iort_add_device_replay(const struct iommu_ops *ops, 747e3d49392SLorenzo Pieralisi struct device *dev) 748d49f2dedSLorenzo Pieralisi { return 0; } 749d49f2dedSLorenzo Pieralisi #endif 750d49f2dedSLorenzo Pieralisi 751bc8648d4SRobin Murphy static int iort_iommu_xlate(struct device *dev, struct acpi_iort_node *node, 752643b8e4dSLorenzo Pieralisi u32 streamid) 753643b8e4dSLorenzo Pieralisi { 754bc8648d4SRobin Murphy const struct iommu_ops *ops; 755643b8e4dSLorenzo Pieralisi struct fwnode_handle *iort_fwnode; 756643b8e4dSLorenzo Pieralisi 757bc8648d4SRobin Murphy if (!node) 758bc8648d4SRobin Murphy return -ENODEV; 759bc8648d4SRobin Murphy 760643b8e4dSLorenzo Pieralisi iort_fwnode = iort_get_fwnode(node); 761643b8e4dSLorenzo Pieralisi if (!iort_fwnode) 762bc8648d4SRobin Murphy return -ENODEV; 763643b8e4dSLorenzo Pieralisi 7645a1bb638SSricharan R /* 7655a1bb638SSricharan R * If the ops look-up fails, this means that either 7665a1bb638SSricharan R * the SMMU drivers have not been probed yet or that 7675a1bb638SSricharan R * the SMMU drivers are not built in the kernel; 7685a1bb638SSricharan R * Depending on whether the SMMU drivers are built-in 7695a1bb638SSricharan R * in the kernel or not, defer the IOMMU configuration 7705a1bb638SSricharan R * or just abort it. 7715a1bb638SSricharan R */ 772bc8648d4SRobin Murphy ops = iommu_ops_from_fwnode(iort_fwnode); 773643b8e4dSLorenzo Pieralisi if (!ops) 7745a1bb638SSricharan R return iort_iommu_driver_enabled(node->type) ? 775bc8648d4SRobin Murphy -EPROBE_DEFER : -ENODEV; 776643b8e4dSLorenzo Pieralisi 777bc8648d4SRobin Murphy return arm_smmu_iort_xlate(dev, streamid, iort_fwnode, ops); 778643b8e4dSLorenzo Pieralisi } 779643b8e4dSLorenzo Pieralisi 780bc8648d4SRobin Murphy struct iort_pci_alias_info { 781bc8648d4SRobin Murphy struct device *dev; 782bc8648d4SRobin Murphy struct acpi_iort_node *node; 783bc8648d4SRobin Murphy }; 784bc8648d4SRobin Murphy 785bc8648d4SRobin Murphy static int iort_pci_iommu_init(struct pci_dev *pdev, u16 alias, void *data) 786bc8648d4SRobin Murphy { 787bc8648d4SRobin Murphy struct iort_pci_alias_info *info = data; 788bc8648d4SRobin Murphy struct acpi_iort_node *parent; 789bc8648d4SRobin Murphy u32 streamid; 790bc8648d4SRobin Murphy 791bc8648d4SRobin Murphy parent = iort_node_map_id(info->node, alias, &streamid, 792bc8648d4SRobin Murphy IORT_IOMMU_TYPE); 793bc8648d4SRobin Murphy return iort_iommu_xlate(info->dev, parent, streamid); 794643b8e4dSLorenzo Pieralisi } 795643b8e4dSLorenzo Pieralisi 79610d8ab2cSLorenzo Pieralisi static int nc_dma_get_range(struct device *dev, u64 *size) 79710d8ab2cSLorenzo Pieralisi { 79810d8ab2cSLorenzo Pieralisi struct acpi_iort_node *node; 79910d8ab2cSLorenzo Pieralisi struct acpi_iort_named_component *ncomp; 80010d8ab2cSLorenzo Pieralisi 80110d8ab2cSLorenzo Pieralisi node = iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT, 80210d8ab2cSLorenzo Pieralisi iort_match_node_callback, dev); 80310d8ab2cSLorenzo Pieralisi if (!node) 80410d8ab2cSLorenzo Pieralisi return -ENODEV; 80510d8ab2cSLorenzo Pieralisi 80610d8ab2cSLorenzo Pieralisi ncomp = (struct acpi_iort_named_component *)node->node_data; 80710d8ab2cSLorenzo Pieralisi 80810d8ab2cSLorenzo Pieralisi *size = ncomp->memory_address_limit >= 64 ? U64_MAX : 80910d8ab2cSLorenzo Pieralisi 1ULL<<ncomp->memory_address_limit; 81010d8ab2cSLorenzo Pieralisi 81110d8ab2cSLorenzo Pieralisi return 0; 81210d8ab2cSLorenzo Pieralisi } 81310d8ab2cSLorenzo Pieralisi 814643b8e4dSLorenzo Pieralisi /** 8157ad42639SLorenzo Pieralisi * iort_dma_setup() - Set-up device DMA parameters. 81618b709beSLorenzo Pieralisi * 81718b709beSLorenzo Pieralisi * @dev: device to configure 8187ad42639SLorenzo Pieralisi * @dma_addr: device DMA address result pointer 8197ad42639SLorenzo Pieralisi * @size: DMA range size result pointer 82018b709beSLorenzo Pieralisi */ 8217ad42639SLorenzo Pieralisi void iort_dma_setup(struct device *dev, u64 *dma_addr, u64 *dma_size) 82218b709beSLorenzo Pieralisi { 8237ad42639SLorenzo Pieralisi u64 mask, dmaaddr = 0, size = 0, offset = 0; 8247ad42639SLorenzo Pieralisi int ret, msb; 8257ad42639SLorenzo Pieralisi 82618b709beSLorenzo Pieralisi /* 82718b709beSLorenzo Pieralisi * Set default coherent_dma_mask to 32 bit. Drivers are expected to 82818b709beSLorenzo Pieralisi * setup the correct supported mask. 82918b709beSLorenzo Pieralisi */ 83018b709beSLorenzo Pieralisi if (!dev->coherent_dma_mask) 83118b709beSLorenzo Pieralisi dev->coherent_dma_mask = DMA_BIT_MASK(32); 83218b709beSLorenzo Pieralisi 83318b709beSLorenzo Pieralisi /* 83418b709beSLorenzo Pieralisi * Set it to coherent_dma_mask by default if the architecture 83518b709beSLorenzo Pieralisi * code has not set it. 83618b709beSLorenzo Pieralisi */ 83718b709beSLorenzo Pieralisi if (!dev->dma_mask) 83818b709beSLorenzo Pieralisi dev->dma_mask = &dev->coherent_dma_mask; 8397ad42639SLorenzo Pieralisi 8407ad42639SLorenzo Pieralisi size = max(dev->coherent_dma_mask, dev->coherent_dma_mask + 1); 8417ad42639SLorenzo Pieralisi 84210d8ab2cSLorenzo Pieralisi if (dev_is_pci(dev)) 8437ad42639SLorenzo Pieralisi ret = acpi_dma_get_range(dev, &dmaaddr, &offset, &size); 84410d8ab2cSLorenzo Pieralisi else 84510d8ab2cSLorenzo Pieralisi ret = nc_dma_get_range(dev, &size); 84610d8ab2cSLorenzo Pieralisi 8477ad42639SLorenzo Pieralisi if (!ret) { 8487ad42639SLorenzo Pieralisi msb = fls64(dmaaddr + size - 1); 8497ad42639SLorenzo Pieralisi /* 8507ad42639SLorenzo Pieralisi * Round-up to the power-of-two mask or set 8517ad42639SLorenzo Pieralisi * the mask to the whole 64-bit address space 8527ad42639SLorenzo Pieralisi * in case the DMA region covers the full 8537ad42639SLorenzo Pieralisi * memory window. 8547ad42639SLorenzo Pieralisi */ 8557ad42639SLorenzo Pieralisi mask = msb == 64 ? U64_MAX : (1ULL << msb) - 1; 8567ad42639SLorenzo Pieralisi /* 8577ad42639SLorenzo Pieralisi * Limit coherent and dma mask based on size 8587ad42639SLorenzo Pieralisi * retrieved from firmware. 8597ad42639SLorenzo Pieralisi */ 8607ad42639SLorenzo Pieralisi dev->coherent_dma_mask = mask; 8617ad42639SLorenzo Pieralisi *dev->dma_mask = mask; 8627ad42639SLorenzo Pieralisi } 8637ad42639SLorenzo Pieralisi 8647ad42639SLorenzo Pieralisi *dma_addr = dmaaddr; 8657ad42639SLorenzo Pieralisi *dma_size = size; 8667ad42639SLorenzo Pieralisi 8677ad42639SLorenzo Pieralisi dev->dma_pfn_offset = PFN_DOWN(offset); 8687ad42639SLorenzo Pieralisi dev_dbg(dev, "dma_pfn_offset(%#08llx)\n", offset); 86918b709beSLorenzo Pieralisi } 87018b709beSLorenzo Pieralisi 87118b709beSLorenzo Pieralisi /** 872643b8e4dSLorenzo Pieralisi * iort_iommu_configure - Set-up IOMMU configuration for a device. 873643b8e4dSLorenzo Pieralisi * 874643b8e4dSLorenzo Pieralisi * @dev: device to configure 875643b8e4dSLorenzo Pieralisi * 876643b8e4dSLorenzo Pieralisi * Returns: iommu_ops pointer on configuration success 877643b8e4dSLorenzo Pieralisi * NULL on configuration failure 878643b8e4dSLorenzo Pieralisi */ 879643b8e4dSLorenzo Pieralisi const struct iommu_ops *iort_iommu_configure(struct device *dev) 880643b8e4dSLorenzo Pieralisi { 881643b8e4dSLorenzo Pieralisi struct acpi_iort_node *node, *parent; 882bc8648d4SRobin Murphy const struct iommu_ops *ops; 883643b8e4dSLorenzo Pieralisi u32 streamid = 0; 884bc8648d4SRobin Murphy int err = -ENODEV; 885643b8e4dSLorenzo Pieralisi 8864dac3210SLorenzo Pieralisi /* 8874dac3210SLorenzo Pieralisi * If we already translated the fwspec there 8884dac3210SLorenzo Pieralisi * is nothing left to do, return the iommu_ops. 8894dac3210SLorenzo Pieralisi */ 8904dac3210SLorenzo Pieralisi ops = iort_fwspec_iommu_ops(dev->iommu_fwspec); 8914dac3210SLorenzo Pieralisi if (ops) 8924dac3210SLorenzo Pieralisi return ops; 8934dac3210SLorenzo Pieralisi 894643b8e4dSLorenzo Pieralisi if (dev_is_pci(dev)) { 895643b8e4dSLorenzo Pieralisi struct pci_bus *bus = to_pci_dev(dev)->bus; 896bc8648d4SRobin Murphy struct iort_pci_alias_info info = { .dev = dev }; 897643b8e4dSLorenzo Pieralisi 898643b8e4dSLorenzo Pieralisi node = iort_scan_node(ACPI_IORT_NODE_PCI_ROOT_COMPLEX, 899643b8e4dSLorenzo Pieralisi iort_match_node_callback, &bus->dev); 900643b8e4dSLorenzo Pieralisi if (!node) 901643b8e4dSLorenzo Pieralisi return NULL; 902643b8e4dSLorenzo Pieralisi 903bc8648d4SRobin Murphy info.node = node; 904bc8648d4SRobin Murphy err = pci_for_each_dma_alias(to_pci_dev(dev), 905bc8648d4SRobin Murphy iort_pci_iommu_init, &info); 906643b8e4dSLorenzo Pieralisi } else { 907643b8e4dSLorenzo Pieralisi int i = 0; 908643b8e4dSLorenzo Pieralisi 909643b8e4dSLorenzo Pieralisi node = iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT, 910643b8e4dSLorenzo Pieralisi iort_match_node_callback, dev); 911643b8e4dSLorenzo Pieralisi if (!node) 912643b8e4dSLorenzo Pieralisi return NULL; 913643b8e4dSLorenzo Pieralisi 914bc8648d4SRobin Murphy do { 9158ca4f1d3SHanjun Guo parent = iort_node_map_platform_id(node, &streamid, 9168ca4f1d3SHanjun Guo IORT_IOMMU_TYPE, 9178ca4f1d3SHanjun Guo i++); 918bc8648d4SRobin Murphy 919bc8648d4SRobin Murphy if (parent) 920bc8648d4SRobin Murphy err = iort_iommu_xlate(dev, parent, streamid); 921bc8648d4SRobin Murphy } while (parent && !err); 922643b8e4dSLorenzo Pieralisi } 923643b8e4dSLorenzo Pieralisi 9245a1bb638SSricharan R /* 9255a1bb638SSricharan R * If we have reason to believe the IOMMU driver missed the initial 9265a1bb638SSricharan R * add_device callback for dev, replay it to get things in order. 9275a1bb638SSricharan R */ 928bc8648d4SRobin Murphy if (!err) { 9294d36037aSArnd Bergmann ops = iort_fwspec_iommu_ops(dev->iommu_fwspec); 930d49f2dedSLorenzo Pieralisi err = iort_add_device_replay(ops, dev); 931bc8648d4SRobin Murphy } 9325a1bb638SSricharan R 933058f8c3fSSricharan R /* Ignore all other errors apart from EPROBE_DEFER */ 934bc8648d4SRobin Murphy if (err == -EPROBE_DEFER) { 935bc8648d4SRobin Murphy ops = ERR_PTR(err); 936bc8648d4SRobin Murphy } else if (err) { 937bc8648d4SRobin Murphy dev_dbg(dev, "Adding to IOMMU failed: %d\n", err); 938058f8c3fSSricharan R ops = NULL; 939058f8c3fSSricharan R } 940058f8c3fSSricharan R 941643b8e4dSLorenzo Pieralisi return ops; 942643b8e4dSLorenzo Pieralisi } 943643b8e4dSLorenzo Pieralisi 944e4dadfa8SLorenzo Pieralisi static void __init acpi_iort_register_irq(int hwirq, const char *name, 945e4dadfa8SLorenzo Pieralisi int trigger, 946e4dadfa8SLorenzo Pieralisi struct resource *res) 947e4dadfa8SLorenzo Pieralisi { 948e4dadfa8SLorenzo Pieralisi int irq = acpi_register_gsi(NULL, hwirq, trigger, 949e4dadfa8SLorenzo Pieralisi ACPI_ACTIVE_HIGH); 950e4dadfa8SLorenzo Pieralisi 951e4dadfa8SLorenzo Pieralisi if (irq <= 0) { 952e4dadfa8SLorenzo Pieralisi pr_err("could not register gsi hwirq %d name [%s]\n", hwirq, 953e4dadfa8SLorenzo Pieralisi name); 954e4dadfa8SLorenzo Pieralisi return; 955e4dadfa8SLorenzo Pieralisi } 956e4dadfa8SLorenzo Pieralisi 957e4dadfa8SLorenzo Pieralisi res->start = irq; 958e4dadfa8SLorenzo Pieralisi res->end = irq; 959e4dadfa8SLorenzo Pieralisi res->flags = IORESOURCE_IRQ; 960e4dadfa8SLorenzo Pieralisi res->name = name; 961e4dadfa8SLorenzo Pieralisi } 962e4dadfa8SLorenzo Pieralisi 963e4dadfa8SLorenzo Pieralisi static int __init arm_smmu_v3_count_resources(struct acpi_iort_node *node) 964e4dadfa8SLorenzo Pieralisi { 965e4dadfa8SLorenzo Pieralisi struct acpi_iort_smmu_v3 *smmu; 966e4dadfa8SLorenzo Pieralisi /* Always present mem resource */ 967e4dadfa8SLorenzo Pieralisi int num_res = 1; 968e4dadfa8SLorenzo Pieralisi 969e4dadfa8SLorenzo Pieralisi /* Retrieve SMMUv3 specific data */ 970e4dadfa8SLorenzo Pieralisi smmu = (struct acpi_iort_smmu_v3 *)node->node_data; 971e4dadfa8SLorenzo Pieralisi 972e4dadfa8SLorenzo Pieralisi if (smmu->event_gsiv) 973e4dadfa8SLorenzo Pieralisi num_res++; 974e4dadfa8SLorenzo Pieralisi 975e4dadfa8SLorenzo Pieralisi if (smmu->pri_gsiv) 976e4dadfa8SLorenzo Pieralisi num_res++; 977e4dadfa8SLorenzo Pieralisi 978e4dadfa8SLorenzo Pieralisi if (smmu->gerr_gsiv) 979e4dadfa8SLorenzo Pieralisi num_res++; 980e4dadfa8SLorenzo Pieralisi 981e4dadfa8SLorenzo Pieralisi if (smmu->sync_gsiv) 982e4dadfa8SLorenzo Pieralisi num_res++; 983e4dadfa8SLorenzo Pieralisi 984e4dadfa8SLorenzo Pieralisi return num_res; 985e4dadfa8SLorenzo Pieralisi } 986e4dadfa8SLorenzo Pieralisi 987f935448aSGeetha Sowjanya static bool arm_smmu_v3_is_combined_irq(struct acpi_iort_smmu_v3 *smmu) 988f935448aSGeetha Sowjanya { 989f935448aSGeetha Sowjanya /* 990f935448aSGeetha Sowjanya * Cavium ThunderX2 implementation doesn't not support unique 991f935448aSGeetha Sowjanya * irq line. Use single irq line for all the SMMUv3 interrupts. 992f935448aSGeetha Sowjanya */ 993f935448aSGeetha Sowjanya if (smmu->model != ACPI_IORT_SMMU_V3_CAVIUM_CN99XX) 994f935448aSGeetha Sowjanya return false; 995f935448aSGeetha Sowjanya 996f935448aSGeetha Sowjanya /* 997f935448aSGeetha Sowjanya * ThunderX2 doesn't support MSIs from the SMMU, so we're checking 998f935448aSGeetha Sowjanya * SPI numbers here. 999f935448aSGeetha Sowjanya */ 1000f935448aSGeetha Sowjanya return smmu->event_gsiv == smmu->pri_gsiv && 1001f935448aSGeetha Sowjanya smmu->event_gsiv == smmu->gerr_gsiv && 1002f935448aSGeetha Sowjanya smmu->event_gsiv == smmu->sync_gsiv; 1003f935448aSGeetha Sowjanya } 1004f935448aSGeetha Sowjanya 1005403e8c7cSLinu Cherian static unsigned long arm_smmu_v3_resource_size(struct acpi_iort_smmu_v3 *smmu) 1006403e8c7cSLinu Cherian { 1007403e8c7cSLinu Cherian /* 1008403e8c7cSLinu Cherian * Override the size, for Cavium ThunderX2 implementation 1009403e8c7cSLinu Cherian * which doesn't support the page 1 SMMU register space. 1010403e8c7cSLinu Cherian */ 1011403e8c7cSLinu Cherian if (smmu->model == ACPI_IORT_SMMU_V3_CAVIUM_CN99XX) 1012403e8c7cSLinu Cherian return SZ_64K; 1013403e8c7cSLinu Cherian 1014403e8c7cSLinu Cherian return SZ_128K; 1015403e8c7cSLinu Cherian } 1016403e8c7cSLinu Cherian 1017e4dadfa8SLorenzo Pieralisi static void __init arm_smmu_v3_init_resources(struct resource *res, 1018e4dadfa8SLorenzo Pieralisi struct acpi_iort_node *node) 1019e4dadfa8SLorenzo Pieralisi { 1020e4dadfa8SLorenzo Pieralisi struct acpi_iort_smmu_v3 *smmu; 1021e4dadfa8SLorenzo Pieralisi int num_res = 0; 1022e4dadfa8SLorenzo Pieralisi 1023e4dadfa8SLorenzo Pieralisi /* Retrieve SMMUv3 specific data */ 1024e4dadfa8SLorenzo Pieralisi smmu = (struct acpi_iort_smmu_v3 *)node->node_data; 1025e4dadfa8SLorenzo Pieralisi 1026e4dadfa8SLorenzo Pieralisi res[num_res].start = smmu->base_address; 1027403e8c7cSLinu Cherian res[num_res].end = smmu->base_address + 1028403e8c7cSLinu Cherian arm_smmu_v3_resource_size(smmu) - 1; 1029e4dadfa8SLorenzo Pieralisi res[num_res].flags = IORESOURCE_MEM; 1030e4dadfa8SLorenzo Pieralisi 1031e4dadfa8SLorenzo Pieralisi num_res++; 1032f935448aSGeetha Sowjanya if (arm_smmu_v3_is_combined_irq(smmu)) { 1033f935448aSGeetha Sowjanya if (smmu->event_gsiv) 1034f935448aSGeetha Sowjanya acpi_iort_register_irq(smmu->event_gsiv, "combined", 1035f935448aSGeetha Sowjanya ACPI_EDGE_SENSITIVE, 1036f935448aSGeetha Sowjanya &res[num_res++]); 1037f935448aSGeetha Sowjanya } else { 1038e4dadfa8SLorenzo Pieralisi 1039e4dadfa8SLorenzo Pieralisi if (smmu->event_gsiv) 1040e4dadfa8SLorenzo Pieralisi acpi_iort_register_irq(smmu->event_gsiv, "eventq", 1041e4dadfa8SLorenzo Pieralisi ACPI_EDGE_SENSITIVE, 1042e4dadfa8SLorenzo Pieralisi &res[num_res++]); 1043e4dadfa8SLorenzo Pieralisi 1044e4dadfa8SLorenzo Pieralisi if (smmu->pri_gsiv) 1045e4dadfa8SLorenzo Pieralisi acpi_iort_register_irq(smmu->pri_gsiv, "priq", 1046e4dadfa8SLorenzo Pieralisi ACPI_EDGE_SENSITIVE, 1047e4dadfa8SLorenzo Pieralisi &res[num_res++]); 1048e4dadfa8SLorenzo Pieralisi 1049e4dadfa8SLorenzo Pieralisi if (smmu->gerr_gsiv) 1050e4dadfa8SLorenzo Pieralisi acpi_iort_register_irq(smmu->gerr_gsiv, "gerror", 1051e4dadfa8SLorenzo Pieralisi ACPI_EDGE_SENSITIVE, 1052e4dadfa8SLorenzo Pieralisi &res[num_res++]); 1053e4dadfa8SLorenzo Pieralisi 1054e4dadfa8SLorenzo Pieralisi if (smmu->sync_gsiv) 1055e4dadfa8SLorenzo Pieralisi acpi_iort_register_irq(smmu->sync_gsiv, "cmdq-sync", 1056e4dadfa8SLorenzo Pieralisi ACPI_EDGE_SENSITIVE, 1057e4dadfa8SLorenzo Pieralisi &res[num_res++]); 1058e4dadfa8SLorenzo Pieralisi } 1059f935448aSGeetha Sowjanya } 1060e4dadfa8SLorenzo Pieralisi 1061e4dadfa8SLorenzo Pieralisi static bool __init arm_smmu_v3_is_coherent(struct acpi_iort_node *node) 1062e4dadfa8SLorenzo Pieralisi { 1063e4dadfa8SLorenzo Pieralisi struct acpi_iort_smmu_v3 *smmu; 1064e4dadfa8SLorenzo Pieralisi 1065e4dadfa8SLorenzo Pieralisi /* Retrieve SMMUv3 specific data */ 1066e4dadfa8SLorenzo Pieralisi smmu = (struct acpi_iort_smmu_v3 *)node->node_data; 1067e4dadfa8SLorenzo Pieralisi 1068e4dadfa8SLorenzo Pieralisi return smmu->flags & ACPI_IORT_SMMU_V3_COHACC_OVERRIDE; 1069e4dadfa8SLorenzo Pieralisi } 1070e4dadfa8SLorenzo Pieralisi 107175808131SLorenzo Pieralisi #if defined(CONFIG_ACPI_NUMA) 10725fe0ce3bSGanapatrao Kulkarni /* 10735fe0ce3bSGanapatrao Kulkarni * set numa proximity domain for smmuv3 device 10745fe0ce3bSGanapatrao Kulkarni */ 10755fe0ce3bSGanapatrao Kulkarni static void __init arm_smmu_v3_set_proximity(struct device *dev, 10765fe0ce3bSGanapatrao Kulkarni struct acpi_iort_node *node) 10775fe0ce3bSGanapatrao Kulkarni { 10785fe0ce3bSGanapatrao Kulkarni struct acpi_iort_smmu_v3 *smmu; 10795fe0ce3bSGanapatrao Kulkarni 10805fe0ce3bSGanapatrao Kulkarni smmu = (struct acpi_iort_smmu_v3 *)node->node_data; 10815fe0ce3bSGanapatrao Kulkarni if (smmu->flags & ACPI_IORT_SMMU_V3_PXM_VALID) { 10825fe0ce3bSGanapatrao Kulkarni set_dev_node(dev, acpi_map_pxm_to_node(smmu->pxm)); 10835fe0ce3bSGanapatrao Kulkarni pr_info("SMMU-v3[%llx] Mapped to Proximity domain %d\n", 10845fe0ce3bSGanapatrao Kulkarni smmu->base_address, 10855fe0ce3bSGanapatrao Kulkarni smmu->pxm); 10865fe0ce3bSGanapatrao Kulkarni } 10875fe0ce3bSGanapatrao Kulkarni } 10885fe0ce3bSGanapatrao Kulkarni #else 10895fe0ce3bSGanapatrao Kulkarni #define arm_smmu_v3_set_proximity NULL 10905fe0ce3bSGanapatrao Kulkarni #endif 10915fe0ce3bSGanapatrao Kulkarni 1092d6fcd3b1SLorenzo Pieralisi static int __init arm_smmu_count_resources(struct acpi_iort_node *node) 1093d6fcd3b1SLorenzo Pieralisi { 1094d6fcd3b1SLorenzo Pieralisi struct acpi_iort_smmu *smmu; 1095d6fcd3b1SLorenzo Pieralisi 1096d6fcd3b1SLorenzo Pieralisi /* Retrieve SMMU specific data */ 1097d6fcd3b1SLorenzo Pieralisi smmu = (struct acpi_iort_smmu *)node->node_data; 1098d6fcd3b1SLorenzo Pieralisi 1099d6fcd3b1SLorenzo Pieralisi /* 1100d6fcd3b1SLorenzo Pieralisi * Only consider the global fault interrupt and ignore the 1101d6fcd3b1SLorenzo Pieralisi * configuration access interrupt. 1102d6fcd3b1SLorenzo Pieralisi * 1103d6fcd3b1SLorenzo Pieralisi * MMIO address and global fault interrupt resources are always 1104d6fcd3b1SLorenzo Pieralisi * present so add them to the context interrupt count as a static 1105d6fcd3b1SLorenzo Pieralisi * value. 1106d6fcd3b1SLorenzo Pieralisi */ 1107d6fcd3b1SLorenzo Pieralisi return smmu->context_interrupt_count + 2; 1108d6fcd3b1SLorenzo Pieralisi } 1109d6fcd3b1SLorenzo Pieralisi 1110d6fcd3b1SLorenzo Pieralisi static void __init arm_smmu_init_resources(struct resource *res, 1111d6fcd3b1SLorenzo Pieralisi struct acpi_iort_node *node) 1112d6fcd3b1SLorenzo Pieralisi { 1113d6fcd3b1SLorenzo Pieralisi struct acpi_iort_smmu *smmu; 1114d6fcd3b1SLorenzo Pieralisi int i, hw_irq, trigger, num_res = 0; 1115d6fcd3b1SLorenzo Pieralisi u64 *ctx_irq, *glb_irq; 1116d6fcd3b1SLorenzo Pieralisi 1117d6fcd3b1SLorenzo Pieralisi /* Retrieve SMMU specific data */ 1118d6fcd3b1SLorenzo Pieralisi smmu = (struct acpi_iort_smmu *)node->node_data; 1119d6fcd3b1SLorenzo Pieralisi 1120d6fcd3b1SLorenzo Pieralisi res[num_res].start = smmu->base_address; 1121d6fcd3b1SLorenzo Pieralisi res[num_res].end = smmu->base_address + smmu->span - 1; 1122d6fcd3b1SLorenzo Pieralisi res[num_res].flags = IORESOURCE_MEM; 1123d6fcd3b1SLorenzo Pieralisi num_res++; 1124d6fcd3b1SLorenzo Pieralisi 1125d6fcd3b1SLorenzo Pieralisi glb_irq = ACPI_ADD_PTR(u64, node, smmu->global_interrupt_offset); 1126d6fcd3b1SLorenzo Pieralisi /* Global IRQs */ 1127d6fcd3b1SLorenzo Pieralisi hw_irq = IORT_IRQ_MASK(glb_irq[0]); 1128d6fcd3b1SLorenzo Pieralisi trigger = IORT_IRQ_TRIGGER_MASK(glb_irq[0]); 1129d6fcd3b1SLorenzo Pieralisi 1130d6fcd3b1SLorenzo Pieralisi acpi_iort_register_irq(hw_irq, "arm-smmu-global", trigger, 1131d6fcd3b1SLorenzo Pieralisi &res[num_res++]); 1132d6fcd3b1SLorenzo Pieralisi 1133d6fcd3b1SLorenzo Pieralisi /* Context IRQs */ 1134d6fcd3b1SLorenzo Pieralisi ctx_irq = ACPI_ADD_PTR(u64, node, smmu->context_interrupt_offset); 1135d6fcd3b1SLorenzo Pieralisi for (i = 0; i < smmu->context_interrupt_count; i++) { 1136d6fcd3b1SLorenzo Pieralisi hw_irq = IORT_IRQ_MASK(ctx_irq[i]); 1137d6fcd3b1SLorenzo Pieralisi trigger = IORT_IRQ_TRIGGER_MASK(ctx_irq[i]); 1138d6fcd3b1SLorenzo Pieralisi 1139d6fcd3b1SLorenzo Pieralisi acpi_iort_register_irq(hw_irq, "arm-smmu-context", trigger, 1140d6fcd3b1SLorenzo Pieralisi &res[num_res++]); 1141d6fcd3b1SLorenzo Pieralisi } 1142d6fcd3b1SLorenzo Pieralisi } 1143d6fcd3b1SLorenzo Pieralisi 1144d6fcd3b1SLorenzo Pieralisi static bool __init arm_smmu_is_coherent(struct acpi_iort_node *node) 1145d6fcd3b1SLorenzo Pieralisi { 1146d6fcd3b1SLorenzo Pieralisi struct acpi_iort_smmu *smmu; 1147d6fcd3b1SLorenzo Pieralisi 1148d6fcd3b1SLorenzo Pieralisi /* Retrieve SMMU specific data */ 1149d6fcd3b1SLorenzo Pieralisi smmu = (struct acpi_iort_smmu *)node->node_data; 1150d6fcd3b1SLorenzo Pieralisi 1151d6fcd3b1SLorenzo Pieralisi return smmu->flags & ACPI_IORT_SMMU_COHERENT_WALK; 1152d6fcd3b1SLorenzo Pieralisi } 1153d6fcd3b1SLorenzo Pieralisi 1154896dd2c3SLorenzo Pieralisi struct iort_dev_config { 1155846f0e9eSLorenzo Pieralisi const char *name; 1156896dd2c3SLorenzo Pieralisi int (*dev_init)(struct acpi_iort_node *node); 1157896dd2c3SLorenzo Pieralisi bool (*dev_is_coherent)(struct acpi_iort_node *node); 1158896dd2c3SLorenzo Pieralisi int (*dev_count_resources)(struct acpi_iort_node *node); 1159896dd2c3SLorenzo Pieralisi void (*dev_init_resources)(struct resource *res, 1160846f0e9eSLorenzo Pieralisi struct acpi_iort_node *node); 1161896dd2c3SLorenzo Pieralisi void (*dev_set_proximity)(struct device *dev, 11625fe0ce3bSGanapatrao Kulkarni struct acpi_iort_node *node); 1163846f0e9eSLorenzo Pieralisi }; 1164846f0e9eSLorenzo Pieralisi 1165896dd2c3SLorenzo Pieralisi static const struct iort_dev_config iort_arm_smmu_v3_cfg __initconst = { 1166e4dadfa8SLorenzo Pieralisi .name = "arm-smmu-v3", 1167896dd2c3SLorenzo Pieralisi .dev_is_coherent = arm_smmu_v3_is_coherent, 1168896dd2c3SLorenzo Pieralisi .dev_count_resources = arm_smmu_v3_count_resources, 1169896dd2c3SLorenzo Pieralisi .dev_init_resources = arm_smmu_v3_init_resources, 1170896dd2c3SLorenzo Pieralisi .dev_set_proximity = arm_smmu_v3_set_proximity, 1171e4dadfa8SLorenzo Pieralisi }; 1172e4dadfa8SLorenzo Pieralisi 1173896dd2c3SLorenzo Pieralisi static const struct iort_dev_config iort_arm_smmu_cfg __initconst = { 1174d6fcd3b1SLorenzo Pieralisi .name = "arm-smmu", 1175896dd2c3SLorenzo Pieralisi .dev_is_coherent = arm_smmu_is_coherent, 1176896dd2c3SLorenzo Pieralisi .dev_count_resources = arm_smmu_count_resources, 1177896dd2c3SLorenzo Pieralisi .dev_init_resources = arm_smmu_init_resources 1178d6fcd3b1SLorenzo Pieralisi }; 1179d6fcd3b1SLorenzo Pieralisi 1180896dd2c3SLorenzo Pieralisi static __init const struct iort_dev_config *iort_get_dev_cfg( 1181e3d49392SLorenzo Pieralisi struct acpi_iort_node *node) 1182846f0e9eSLorenzo Pieralisi { 1183e4dadfa8SLorenzo Pieralisi switch (node->type) { 1184e4dadfa8SLorenzo Pieralisi case ACPI_IORT_NODE_SMMU_V3: 1185e4dadfa8SLorenzo Pieralisi return &iort_arm_smmu_v3_cfg; 1186d6fcd3b1SLorenzo Pieralisi case ACPI_IORT_NODE_SMMU: 1187d6fcd3b1SLorenzo Pieralisi return &iort_arm_smmu_cfg; 1188e4dadfa8SLorenzo Pieralisi default: 1189846f0e9eSLorenzo Pieralisi return NULL; 1190846f0e9eSLorenzo Pieralisi } 1191e4dadfa8SLorenzo Pieralisi } 1192846f0e9eSLorenzo Pieralisi 1193846f0e9eSLorenzo Pieralisi /** 1194896dd2c3SLorenzo Pieralisi * iort_add_platform_device() - Allocate a platform device for IORT node 1195896dd2c3SLorenzo Pieralisi * @node: Pointer to device ACPI IORT node 1196846f0e9eSLorenzo Pieralisi * 1197846f0e9eSLorenzo Pieralisi * Returns: 0 on success, <0 failure 1198846f0e9eSLorenzo Pieralisi */ 1199896dd2c3SLorenzo Pieralisi static int __init iort_add_platform_device(struct acpi_iort_node *node, 1200896dd2c3SLorenzo Pieralisi const struct iort_dev_config *ops) 1201846f0e9eSLorenzo Pieralisi { 1202846f0e9eSLorenzo Pieralisi struct fwnode_handle *fwnode; 1203846f0e9eSLorenzo Pieralisi struct platform_device *pdev; 1204846f0e9eSLorenzo Pieralisi struct resource *r; 1205846f0e9eSLorenzo Pieralisi enum dev_dma_attr attr; 1206846f0e9eSLorenzo Pieralisi int ret, count; 1207846f0e9eSLorenzo Pieralisi 1208846f0e9eSLorenzo Pieralisi pdev = platform_device_alloc(ops->name, PLATFORM_DEVID_AUTO); 1209846f0e9eSLorenzo Pieralisi if (!pdev) 12105e5afa6cSDan Carpenter return -ENOMEM; 1211846f0e9eSLorenzo Pieralisi 1212896dd2c3SLorenzo Pieralisi if (ops->dev_set_proximity) 1213896dd2c3SLorenzo Pieralisi ops->dev_set_proximity(&pdev->dev, node); 12145fe0ce3bSGanapatrao Kulkarni 1215896dd2c3SLorenzo Pieralisi count = ops->dev_count_resources(node); 1216846f0e9eSLorenzo Pieralisi 1217846f0e9eSLorenzo Pieralisi r = kcalloc(count, sizeof(*r), GFP_KERNEL); 1218846f0e9eSLorenzo Pieralisi if (!r) { 1219846f0e9eSLorenzo Pieralisi ret = -ENOMEM; 1220846f0e9eSLorenzo Pieralisi goto dev_put; 1221846f0e9eSLorenzo Pieralisi } 1222846f0e9eSLorenzo Pieralisi 1223896dd2c3SLorenzo Pieralisi ops->dev_init_resources(r, node); 1224846f0e9eSLorenzo Pieralisi 1225846f0e9eSLorenzo Pieralisi ret = platform_device_add_resources(pdev, r, count); 1226846f0e9eSLorenzo Pieralisi /* 1227846f0e9eSLorenzo Pieralisi * Resources are duplicated in platform_device_add_resources, 1228846f0e9eSLorenzo Pieralisi * free their allocated memory 1229846f0e9eSLorenzo Pieralisi */ 1230846f0e9eSLorenzo Pieralisi kfree(r); 1231846f0e9eSLorenzo Pieralisi 1232846f0e9eSLorenzo Pieralisi if (ret) 1233846f0e9eSLorenzo Pieralisi goto dev_put; 1234846f0e9eSLorenzo Pieralisi 1235846f0e9eSLorenzo Pieralisi /* 1236846f0e9eSLorenzo Pieralisi * Add a copy of IORT node pointer to platform_data to 1237846f0e9eSLorenzo Pieralisi * be used to retrieve IORT data information. 1238846f0e9eSLorenzo Pieralisi */ 1239846f0e9eSLorenzo Pieralisi ret = platform_device_add_data(pdev, &node, sizeof(node)); 1240846f0e9eSLorenzo Pieralisi if (ret) 1241846f0e9eSLorenzo Pieralisi goto dev_put; 1242846f0e9eSLorenzo Pieralisi 1243846f0e9eSLorenzo Pieralisi /* 1244846f0e9eSLorenzo Pieralisi * We expect the dma masks to be equivalent for 1245846f0e9eSLorenzo Pieralisi * all SMMUs set-ups 1246846f0e9eSLorenzo Pieralisi */ 1247846f0e9eSLorenzo Pieralisi pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask; 1248846f0e9eSLorenzo Pieralisi 1249846f0e9eSLorenzo Pieralisi fwnode = iort_get_fwnode(node); 1250846f0e9eSLorenzo Pieralisi 1251846f0e9eSLorenzo Pieralisi if (!fwnode) { 1252846f0e9eSLorenzo Pieralisi ret = -ENODEV; 1253846f0e9eSLorenzo Pieralisi goto dev_put; 1254846f0e9eSLorenzo Pieralisi } 1255846f0e9eSLorenzo Pieralisi 1256846f0e9eSLorenzo Pieralisi pdev->dev.fwnode = fwnode; 1257846f0e9eSLorenzo Pieralisi 1258896dd2c3SLorenzo Pieralisi attr = ops->dev_is_coherent && ops->dev_is_coherent(node) ? 1259846f0e9eSLorenzo Pieralisi DEV_DMA_COHERENT : DEV_DMA_NON_COHERENT; 1260846f0e9eSLorenzo Pieralisi 1261846f0e9eSLorenzo Pieralisi /* Configure DMA for the page table walker */ 1262846f0e9eSLorenzo Pieralisi acpi_dma_configure(&pdev->dev, attr); 1263846f0e9eSLorenzo Pieralisi 1264846f0e9eSLorenzo Pieralisi ret = platform_device_add(pdev); 1265846f0e9eSLorenzo Pieralisi if (ret) 1266846f0e9eSLorenzo Pieralisi goto dma_deconfigure; 1267846f0e9eSLorenzo Pieralisi 1268846f0e9eSLorenzo Pieralisi return 0; 1269846f0e9eSLorenzo Pieralisi 1270846f0e9eSLorenzo Pieralisi dma_deconfigure: 1271846f0e9eSLorenzo Pieralisi acpi_dma_deconfigure(&pdev->dev); 1272846f0e9eSLorenzo Pieralisi dev_put: 1273846f0e9eSLorenzo Pieralisi platform_device_put(pdev); 1274846f0e9eSLorenzo Pieralisi 1275846f0e9eSLorenzo Pieralisi return ret; 1276846f0e9eSLorenzo Pieralisi } 1277846f0e9eSLorenzo Pieralisi 1278846f0e9eSLorenzo Pieralisi static void __init iort_init_platform_devices(void) 1279846f0e9eSLorenzo Pieralisi { 1280846f0e9eSLorenzo Pieralisi struct acpi_iort_node *iort_node, *iort_end; 1281846f0e9eSLorenzo Pieralisi struct acpi_table_iort *iort; 1282846f0e9eSLorenzo Pieralisi struct fwnode_handle *fwnode; 1283846f0e9eSLorenzo Pieralisi int i, ret; 1284896dd2c3SLorenzo Pieralisi const struct iort_dev_config *ops; 1285846f0e9eSLorenzo Pieralisi 1286846f0e9eSLorenzo Pieralisi /* 1287846f0e9eSLorenzo Pieralisi * iort_table and iort both point to the start of IORT table, but 1288846f0e9eSLorenzo Pieralisi * have different struct types 1289846f0e9eSLorenzo Pieralisi */ 1290846f0e9eSLorenzo Pieralisi iort = (struct acpi_table_iort *)iort_table; 1291846f0e9eSLorenzo Pieralisi 1292846f0e9eSLorenzo Pieralisi /* Get the first IORT node */ 1293846f0e9eSLorenzo Pieralisi iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort, 1294846f0e9eSLorenzo Pieralisi iort->node_offset); 1295846f0e9eSLorenzo Pieralisi iort_end = ACPI_ADD_PTR(struct acpi_iort_node, iort, 1296846f0e9eSLorenzo Pieralisi iort_table->length); 1297846f0e9eSLorenzo Pieralisi 1298846f0e9eSLorenzo Pieralisi for (i = 0; i < iort->node_count; i++) { 1299846f0e9eSLorenzo Pieralisi if (iort_node >= iort_end) { 1300846f0e9eSLorenzo Pieralisi pr_err("iort node pointer overflows, bad table\n"); 1301846f0e9eSLorenzo Pieralisi return; 1302846f0e9eSLorenzo Pieralisi } 1303846f0e9eSLorenzo Pieralisi 1304896dd2c3SLorenzo Pieralisi ops = iort_get_dev_cfg(iort_node); 1305896dd2c3SLorenzo Pieralisi if (ops) { 1306846f0e9eSLorenzo Pieralisi fwnode = acpi_alloc_fwnode_static(); 1307846f0e9eSLorenzo Pieralisi if (!fwnode) 1308846f0e9eSLorenzo Pieralisi return; 1309846f0e9eSLorenzo Pieralisi 1310846f0e9eSLorenzo Pieralisi iort_set_fwnode(iort_node, fwnode); 1311846f0e9eSLorenzo Pieralisi 1312896dd2c3SLorenzo Pieralisi ret = iort_add_platform_device(iort_node, ops); 1313846f0e9eSLorenzo Pieralisi if (ret) { 1314846f0e9eSLorenzo Pieralisi iort_delete_fwnode(iort_node); 1315846f0e9eSLorenzo Pieralisi acpi_free_fwnode_static(fwnode); 1316846f0e9eSLorenzo Pieralisi return; 1317846f0e9eSLorenzo Pieralisi } 1318846f0e9eSLorenzo Pieralisi } 1319846f0e9eSLorenzo Pieralisi 1320846f0e9eSLorenzo Pieralisi iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort_node, 1321846f0e9eSLorenzo Pieralisi iort_node->length); 1322846f0e9eSLorenzo Pieralisi } 1323846f0e9eSLorenzo Pieralisi } 1324846f0e9eSLorenzo Pieralisi 132588ef16d8STomasz Nowicki void __init acpi_iort_init(void) 132688ef16d8STomasz Nowicki { 132788ef16d8STomasz Nowicki acpi_status status; 132888ef16d8STomasz Nowicki 132988ef16d8STomasz Nowicki status = acpi_get_table(ACPI_SIG_IORT, 0, &iort_table); 133034ceea27SLorenzo Pieralisi if (ACPI_FAILURE(status)) { 133134ceea27SLorenzo Pieralisi if (status != AE_NOT_FOUND) { 133288ef16d8STomasz Nowicki const char *msg = acpi_format_exception(status); 133334ceea27SLorenzo Pieralisi 133488ef16d8STomasz Nowicki pr_err("Failed to get table, %s\n", msg); 133588ef16d8STomasz Nowicki } 133634ceea27SLorenzo Pieralisi 133734ceea27SLorenzo Pieralisi return; 133834ceea27SLorenzo Pieralisi } 133934ceea27SLorenzo Pieralisi 1340846f0e9eSLorenzo Pieralisi iort_init_platform_devices(); 134188ef16d8STomasz Nowicki } 1342