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 */ 91*e3d49392SLorenzo Pieralisi static inline struct fwnode_handle *iort_get_fwnode( 92*e3d49392SLorenzo 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 12988ef16d8STomasz Nowicki typedef acpi_status (*iort_find_node_callback) 13088ef16d8STomasz Nowicki (struct acpi_iort_node *node, void *context); 13188ef16d8STomasz Nowicki 13288ef16d8STomasz Nowicki /* Root pointer to the mapped IORT table */ 13388ef16d8STomasz Nowicki static struct acpi_table_header *iort_table; 13488ef16d8STomasz Nowicki 13588ef16d8STomasz Nowicki static LIST_HEAD(iort_msi_chip_list); 13688ef16d8STomasz Nowicki static DEFINE_SPINLOCK(iort_msi_chip_lock); 13788ef16d8STomasz Nowicki 1384bf2efd2STomasz Nowicki /** 1394bf2efd2STomasz Nowicki * iort_register_domain_token() - register domain token and related ITS ID 1404bf2efd2STomasz Nowicki * to the list from where we can get it back later on. 1414bf2efd2STomasz Nowicki * @trans_id: ITS ID. 1424bf2efd2STomasz Nowicki * @fw_node: Domain token. 1434bf2efd2STomasz Nowicki * 1444bf2efd2STomasz Nowicki * Returns: 0 on success, -ENOMEM if no memory when allocating list element 1454bf2efd2STomasz Nowicki */ 1464bf2efd2STomasz Nowicki int iort_register_domain_token(int trans_id, struct fwnode_handle *fw_node) 1474bf2efd2STomasz Nowicki { 1484bf2efd2STomasz Nowicki struct iort_its_msi_chip *its_msi_chip; 1494bf2efd2STomasz Nowicki 1504bf2efd2STomasz Nowicki its_msi_chip = kzalloc(sizeof(*its_msi_chip), GFP_KERNEL); 1514bf2efd2STomasz Nowicki if (!its_msi_chip) 1524bf2efd2STomasz Nowicki return -ENOMEM; 1534bf2efd2STomasz Nowicki 1544bf2efd2STomasz Nowicki its_msi_chip->fw_node = fw_node; 1554bf2efd2STomasz Nowicki its_msi_chip->translation_id = trans_id; 1564bf2efd2STomasz Nowicki 1574bf2efd2STomasz Nowicki spin_lock(&iort_msi_chip_lock); 1584bf2efd2STomasz Nowicki list_add(&its_msi_chip->list, &iort_msi_chip_list); 1594bf2efd2STomasz Nowicki spin_unlock(&iort_msi_chip_lock); 1604bf2efd2STomasz Nowicki 1614bf2efd2STomasz Nowicki return 0; 1624bf2efd2STomasz Nowicki } 1634bf2efd2STomasz Nowicki 1644bf2efd2STomasz Nowicki /** 1654bf2efd2STomasz Nowicki * iort_deregister_domain_token() - Deregister domain token based on ITS ID 1664bf2efd2STomasz Nowicki * @trans_id: ITS ID. 1674bf2efd2STomasz Nowicki * 1684bf2efd2STomasz Nowicki * Returns: none. 1694bf2efd2STomasz Nowicki */ 1704bf2efd2STomasz Nowicki void iort_deregister_domain_token(int trans_id) 1714bf2efd2STomasz Nowicki { 1724bf2efd2STomasz Nowicki struct iort_its_msi_chip *its_msi_chip, *t; 1734bf2efd2STomasz Nowicki 1744bf2efd2STomasz Nowicki spin_lock(&iort_msi_chip_lock); 1754bf2efd2STomasz Nowicki list_for_each_entry_safe(its_msi_chip, t, &iort_msi_chip_list, list) { 1764bf2efd2STomasz Nowicki if (its_msi_chip->translation_id == trans_id) { 1774bf2efd2STomasz Nowicki list_del(&its_msi_chip->list); 1784bf2efd2STomasz Nowicki kfree(its_msi_chip); 1794bf2efd2STomasz Nowicki break; 1804bf2efd2STomasz Nowicki } 1814bf2efd2STomasz Nowicki } 1824bf2efd2STomasz Nowicki spin_unlock(&iort_msi_chip_lock); 1834bf2efd2STomasz Nowicki } 1844bf2efd2STomasz Nowicki 1854bf2efd2STomasz Nowicki /** 1864bf2efd2STomasz Nowicki * iort_find_domain_token() - Find domain token based on given ITS ID 1874bf2efd2STomasz Nowicki * @trans_id: ITS ID. 1884bf2efd2STomasz Nowicki * 1894bf2efd2STomasz Nowicki * Returns: domain token when find on the list, NULL otherwise 1904bf2efd2STomasz Nowicki */ 1914bf2efd2STomasz Nowicki struct fwnode_handle *iort_find_domain_token(int trans_id) 1924bf2efd2STomasz Nowicki { 1934bf2efd2STomasz Nowicki struct fwnode_handle *fw_node = NULL; 1944bf2efd2STomasz Nowicki struct iort_its_msi_chip *its_msi_chip; 1954bf2efd2STomasz Nowicki 1964bf2efd2STomasz Nowicki spin_lock(&iort_msi_chip_lock); 1974bf2efd2STomasz Nowicki list_for_each_entry(its_msi_chip, &iort_msi_chip_list, list) { 1984bf2efd2STomasz Nowicki if (its_msi_chip->translation_id == trans_id) { 1994bf2efd2STomasz Nowicki fw_node = its_msi_chip->fw_node; 2004bf2efd2STomasz Nowicki break; 2014bf2efd2STomasz Nowicki } 2024bf2efd2STomasz Nowicki } 2034bf2efd2STomasz Nowicki spin_unlock(&iort_msi_chip_lock); 2044bf2efd2STomasz Nowicki 2054bf2efd2STomasz Nowicki return fw_node; 2064bf2efd2STomasz Nowicki } 2074bf2efd2STomasz Nowicki 20888ef16d8STomasz Nowicki static struct acpi_iort_node *iort_scan_node(enum acpi_iort_node_type type, 20988ef16d8STomasz Nowicki iort_find_node_callback callback, 21088ef16d8STomasz Nowicki void *context) 21188ef16d8STomasz Nowicki { 21288ef16d8STomasz Nowicki struct acpi_iort_node *iort_node, *iort_end; 21388ef16d8STomasz Nowicki struct acpi_table_iort *iort; 21488ef16d8STomasz Nowicki int i; 21588ef16d8STomasz Nowicki 21688ef16d8STomasz Nowicki if (!iort_table) 21788ef16d8STomasz Nowicki return NULL; 21888ef16d8STomasz Nowicki 21988ef16d8STomasz Nowicki /* Get the first IORT node */ 22088ef16d8STomasz Nowicki iort = (struct acpi_table_iort *)iort_table; 22188ef16d8STomasz Nowicki iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort, 22288ef16d8STomasz Nowicki iort->node_offset); 22388ef16d8STomasz Nowicki iort_end = ACPI_ADD_PTR(struct acpi_iort_node, iort_table, 22488ef16d8STomasz Nowicki iort_table->length); 22588ef16d8STomasz Nowicki 22688ef16d8STomasz Nowicki for (i = 0; i < iort->node_count; i++) { 22788ef16d8STomasz Nowicki if (WARN_TAINT(iort_node >= iort_end, TAINT_FIRMWARE_WORKAROUND, 22888ef16d8STomasz Nowicki "IORT node pointer overflows, bad table!\n")) 22988ef16d8STomasz Nowicki return NULL; 23088ef16d8STomasz Nowicki 23188ef16d8STomasz Nowicki if (iort_node->type == type && 23288ef16d8STomasz Nowicki ACPI_SUCCESS(callback(iort_node, context))) 23388ef16d8STomasz Nowicki return iort_node; 23488ef16d8STomasz Nowicki 23588ef16d8STomasz Nowicki iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort_node, 23688ef16d8STomasz Nowicki iort_node->length); 23788ef16d8STomasz Nowicki } 23888ef16d8STomasz Nowicki 23988ef16d8STomasz Nowicki return NULL; 24088ef16d8STomasz Nowicki } 24188ef16d8STomasz Nowicki 24288ef16d8STomasz Nowicki static acpi_status iort_match_node_callback(struct acpi_iort_node *node, 24388ef16d8STomasz Nowicki void *context) 24488ef16d8STomasz Nowicki { 24588ef16d8STomasz Nowicki struct device *dev = context; 246c92bdfe8SHanjun Guo acpi_status status = AE_NOT_FOUND; 24788ef16d8STomasz Nowicki 24888ef16d8STomasz Nowicki if (node->type == ACPI_IORT_NODE_NAMED_COMPONENT) { 24988ef16d8STomasz Nowicki struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL }; 25088ef16d8STomasz Nowicki struct acpi_device *adev = to_acpi_device_node(dev->fwnode); 25188ef16d8STomasz Nowicki struct acpi_iort_named_component *ncomp; 25288ef16d8STomasz Nowicki 253c92bdfe8SHanjun Guo if (!adev) 25488ef16d8STomasz Nowicki goto out; 25588ef16d8STomasz Nowicki 25688ef16d8STomasz Nowicki status = acpi_get_name(adev->handle, ACPI_FULL_PATHNAME, &buf); 25788ef16d8STomasz Nowicki if (ACPI_FAILURE(status)) { 25888ef16d8STomasz Nowicki dev_warn(dev, "Can't get device full path name\n"); 25988ef16d8STomasz Nowicki goto out; 26088ef16d8STomasz Nowicki } 26188ef16d8STomasz Nowicki 26288ef16d8STomasz Nowicki ncomp = (struct acpi_iort_named_component *)node->node_data; 26388ef16d8STomasz Nowicki status = !strcmp(ncomp->device_name, buf.pointer) ? 26488ef16d8STomasz Nowicki AE_OK : AE_NOT_FOUND; 26588ef16d8STomasz Nowicki acpi_os_free(buf.pointer); 26688ef16d8STomasz Nowicki } else if (node->type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX) { 26788ef16d8STomasz Nowicki struct acpi_iort_root_complex *pci_rc; 26888ef16d8STomasz Nowicki struct pci_bus *bus; 26988ef16d8STomasz Nowicki 27088ef16d8STomasz Nowicki bus = to_pci_bus(dev); 27188ef16d8STomasz Nowicki pci_rc = (struct acpi_iort_root_complex *)node->node_data; 27288ef16d8STomasz Nowicki 27388ef16d8STomasz Nowicki /* 27488ef16d8STomasz Nowicki * It is assumed that PCI segment numbers maps one-to-one 27588ef16d8STomasz Nowicki * with root complexes. Each segment number can represent only 27688ef16d8STomasz Nowicki * one root complex. 27788ef16d8STomasz Nowicki */ 27888ef16d8STomasz Nowicki status = pci_rc->pci_segment_number == pci_domain_nr(bus) ? 27988ef16d8STomasz Nowicki AE_OK : AE_NOT_FOUND; 28088ef16d8STomasz Nowicki } 28188ef16d8STomasz Nowicki out: 28288ef16d8STomasz Nowicki return status; 28388ef16d8STomasz Nowicki } 28488ef16d8STomasz Nowicki 28588ef16d8STomasz Nowicki static int iort_id_map(struct acpi_iort_id_mapping *map, u8 type, u32 rid_in, 28688ef16d8STomasz Nowicki u32 *rid_out) 28788ef16d8STomasz Nowicki { 28888ef16d8STomasz Nowicki /* Single mapping does not care for input id */ 28988ef16d8STomasz Nowicki if (map->flags & ACPI_IORT_ID_SINGLE_MAPPING) { 29088ef16d8STomasz Nowicki if (type == ACPI_IORT_NODE_NAMED_COMPONENT || 29188ef16d8STomasz Nowicki type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX) { 29288ef16d8STomasz Nowicki *rid_out = map->output_base; 29388ef16d8STomasz Nowicki return 0; 29488ef16d8STomasz Nowicki } 29588ef16d8STomasz Nowicki 29688ef16d8STomasz Nowicki pr_warn(FW_BUG "[map %p] SINGLE MAPPING flag not allowed for node type %d, skipping ID map\n", 29788ef16d8STomasz Nowicki map, type); 29888ef16d8STomasz Nowicki return -ENXIO; 29988ef16d8STomasz Nowicki } 30088ef16d8STomasz Nowicki 30188ef16d8STomasz Nowicki if (rid_in < map->input_base || 30288ef16d8STomasz Nowicki (rid_in >= map->input_base + map->id_count)) 30388ef16d8STomasz Nowicki return -ENXIO; 30488ef16d8STomasz Nowicki 30588ef16d8STomasz Nowicki *rid_out = map->output_base + (rid_in - map->input_base); 30688ef16d8STomasz Nowicki return 0; 30788ef16d8STomasz Nowicki } 30888ef16d8STomasz Nowicki 309*e3d49392SLorenzo Pieralisi static struct acpi_iort_node *iort_node_get_id(struct acpi_iort_node *node, 3108ca4f1d3SHanjun Guo u32 *id_out, int index) 311618f535aSLorenzo Pieralisi { 312618f535aSLorenzo Pieralisi struct acpi_iort_node *parent; 313618f535aSLorenzo Pieralisi struct acpi_iort_id_mapping *map; 314618f535aSLorenzo Pieralisi 315618f535aSLorenzo Pieralisi if (!node->mapping_offset || !node->mapping_count || 316618f535aSLorenzo Pieralisi index >= node->mapping_count) 317618f535aSLorenzo Pieralisi return NULL; 318618f535aSLorenzo Pieralisi 319618f535aSLorenzo Pieralisi map = ACPI_ADD_PTR(struct acpi_iort_id_mapping, node, 320030abd8aSLorenzo Pieralisi node->mapping_offset + index * sizeof(*map)); 321618f535aSLorenzo Pieralisi 322618f535aSLorenzo Pieralisi /* Firmware bug! */ 323618f535aSLorenzo Pieralisi if (!map->output_reference) { 324618f535aSLorenzo Pieralisi pr_err(FW_BUG "[node %p type %d] ID map has NULL parent reference\n", 325618f535aSLorenzo Pieralisi node, node->type); 326618f535aSLorenzo Pieralisi return NULL; 327618f535aSLorenzo Pieralisi } 328618f535aSLorenzo Pieralisi 329618f535aSLorenzo Pieralisi parent = ACPI_ADD_PTR(struct acpi_iort_node, iort_table, 330618f535aSLorenzo Pieralisi map->output_reference); 331618f535aSLorenzo Pieralisi 332030abd8aSLorenzo Pieralisi if (map->flags & ACPI_IORT_ID_SINGLE_MAPPING) { 333618f535aSLorenzo Pieralisi if (node->type == ACPI_IORT_NODE_NAMED_COMPONENT || 334618f535aSLorenzo Pieralisi node->type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX) { 335030abd8aSLorenzo Pieralisi *id_out = map->output_base; 336618f535aSLorenzo Pieralisi return parent; 337618f535aSLorenzo Pieralisi } 338618f535aSLorenzo Pieralisi } 339618f535aSLorenzo Pieralisi 340618f535aSLorenzo Pieralisi return NULL; 341618f535aSLorenzo Pieralisi } 342618f535aSLorenzo Pieralisi 343697f6093SHanjun Guo static struct acpi_iort_node *iort_node_map_id(struct acpi_iort_node *node, 344697f6093SHanjun Guo u32 id_in, u32 *id_out, 345ea50b524SLorenzo Pieralisi u8 type_mask) 34688ef16d8STomasz Nowicki { 347697f6093SHanjun Guo u32 id = id_in; 34888ef16d8STomasz Nowicki 34988ef16d8STomasz Nowicki /* Parse the ID mapping tree to find specified node type */ 35088ef16d8STomasz Nowicki while (node) { 35188ef16d8STomasz Nowicki struct acpi_iort_id_mapping *map; 35288ef16d8STomasz Nowicki int i; 35388ef16d8STomasz Nowicki 354ea50b524SLorenzo Pieralisi if (IORT_TYPE_MASK(node->type) & type_mask) { 355697f6093SHanjun Guo if (id_out) 356697f6093SHanjun Guo *id_out = id; 35788ef16d8STomasz Nowicki return node; 35888ef16d8STomasz Nowicki } 35988ef16d8STomasz Nowicki 36088ef16d8STomasz Nowicki if (!node->mapping_offset || !node->mapping_count) 36188ef16d8STomasz Nowicki goto fail_map; 36288ef16d8STomasz Nowicki 36388ef16d8STomasz Nowicki map = ACPI_ADD_PTR(struct acpi_iort_id_mapping, node, 36488ef16d8STomasz Nowicki node->mapping_offset); 36588ef16d8STomasz Nowicki 36688ef16d8STomasz Nowicki /* Firmware bug! */ 36788ef16d8STomasz Nowicki if (!map->output_reference) { 36888ef16d8STomasz Nowicki pr_err(FW_BUG "[node %p type %d] ID map has NULL parent reference\n", 36988ef16d8STomasz Nowicki node, node->type); 37088ef16d8STomasz Nowicki goto fail_map; 37188ef16d8STomasz Nowicki } 37288ef16d8STomasz Nowicki 373697f6093SHanjun Guo /* Do the ID translation */ 37488ef16d8STomasz Nowicki for (i = 0; i < node->mapping_count; i++, map++) { 375697f6093SHanjun Guo if (!iort_id_map(map, node->type, id, &id)) 37688ef16d8STomasz Nowicki break; 37788ef16d8STomasz Nowicki } 37888ef16d8STomasz Nowicki 37988ef16d8STomasz Nowicki if (i == node->mapping_count) 38088ef16d8STomasz Nowicki goto fail_map; 38188ef16d8STomasz Nowicki 38288ef16d8STomasz Nowicki node = ACPI_ADD_PTR(struct acpi_iort_node, iort_table, 38388ef16d8STomasz Nowicki map->output_reference); 38488ef16d8STomasz Nowicki } 38588ef16d8STomasz Nowicki 38688ef16d8STomasz Nowicki fail_map: 387697f6093SHanjun Guo /* Map input ID to output ID unchanged on mapping failure */ 388697f6093SHanjun Guo if (id_out) 389697f6093SHanjun Guo *id_out = id_in; 39088ef16d8STomasz Nowicki 39188ef16d8STomasz Nowicki return NULL; 39288ef16d8STomasz Nowicki } 39388ef16d8STomasz Nowicki 394*e3d49392SLorenzo Pieralisi static struct acpi_iort_node *iort_node_map_platform_id( 395*e3d49392SLorenzo Pieralisi struct acpi_iort_node *node, u32 *id_out, u8 type_mask, 3968ca4f1d3SHanjun Guo int index) 3978ca4f1d3SHanjun Guo { 3988ca4f1d3SHanjun Guo struct acpi_iort_node *parent; 3998ca4f1d3SHanjun Guo u32 id; 4008ca4f1d3SHanjun Guo 4018ca4f1d3SHanjun Guo /* step 1: retrieve the initial dev id */ 4028ca4f1d3SHanjun Guo parent = iort_node_get_id(node, &id, index); 4038ca4f1d3SHanjun Guo if (!parent) 4048ca4f1d3SHanjun Guo return NULL; 4058ca4f1d3SHanjun Guo 4068ca4f1d3SHanjun Guo /* 4078ca4f1d3SHanjun Guo * optional step 2: map the initial dev id if its parent is not 4088ca4f1d3SHanjun Guo * the target type we want, map it again for the use cases such 4098ca4f1d3SHanjun Guo * as NC (named component) -> SMMU -> ITS. If the type is matched, 4108ca4f1d3SHanjun Guo * return the initial dev id and its parent pointer directly. 4118ca4f1d3SHanjun Guo */ 4128ca4f1d3SHanjun Guo if (!(IORT_TYPE_MASK(parent->type) & type_mask)) 4138ca4f1d3SHanjun Guo parent = iort_node_map_id(parent, id, id_out, type_mask); 4148ca4f1d3SHanjun Guo else 4158ca4f1d3SHanjun Guo if (id_out) 4168ca4f1d3SHanjun Guo *id_out = id; 4178ca4f1d3SHanjun Guo 4188ca4f1d3SHanjun Guo return parent; 4198ca4f1d3SHanjun Guo } 4208ca4f1d3SHanjun Guo 42188ef16d8STomasz Nowicki static struct acpi_iort_node *iort_find_dev_node(struct device *dev) 42288ef16d8STomasz Nowicki { 42388ef16d8STomasz Nowicki struct pci_bus *pbus; 42488ef16d8STomasz Nowicki 42588ef16d8STomasz Nowicki if (!dev_is_pci(dev)) 42688ef16d8STomasz Nowicki return iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT, 42788ef16d8STomasz Nowicki iort_match_node_callback, dev); 42888ef16d8STomasz Nowicki 42988ef16d8STomasz Nowicki /* Find a PCI root bus */ 43088ef16d8STomasz Nowicki pbus = to_pci_dev(dev)->bus; 43188ef16d8STomasz Nowicki while (!pci_is_root_bus(pbus)) 43288ef16d8STomasz Nowicki pbus = pbus->parent; 43388ef16d8STomasz Nowicki 43488ef16d8STomasz Nowicki return iort_scan_node(ACPI_IORT_NODE_PCI_ROOT_COMPLEX, 43588ef16d8STomasz Nowicki iort_match_node_callback, &pbus->dev); 43688ef16d8STomasz Nowicki } 43788ef16d8STomasz Nowicki 4384bf2efd2STomasz Nowicki /** 4394bf2efd2STomasz Nowicki * iort_msi_map_rid() - Map a MSI requester ID for a device 4404bf2efd2STomasz Nowicki * @dev: The device for which the mapping is to be done. 4414bf2efd2STomasz Nowicki * @req_id: The device requester ID. 4424bf2efd2STomasz Nowicki * 4434bf2efd2STomasz Nowicki * Returns: mapped MSI RID on success, input requester ID otherwise 4444bf2efd2STomasz Nowicki */ 4454bf2efd2STomasz Nowicki u32 iort_msi_map_rid(struct device *dev, u32 req_id) 4464bf2efd2STomasz Nowicki { 4474bf2efd2STomasz Nowicki struct acpi_iort_node *node; 4484bf2efd2STomasz Nowicki u32 dev_id; 4494bf2efd2STomasz Nowicki 4504bf2efd2STomasz Nowicki node = iort_find_dev_node(dev); 4514bf2efd2STomasz Nowicki if (!node) 4524bf2efd2STomasz Nowicki return req_id; 4534bf2efd2STomasz Nowicki 454697f6093SHanjun Guo iort_node_map_id(node, req_id, &dev_id, IORT_MSI_TYPE); 4554bf2efd2STomasz Nowicki return dev_id; 4564bf2efd2STomasz Nowicki } 4574bf2efd2STomasz Nowicki 4584bf2efd2STomasz Nowicki /** 459ae7c1838SHanjun Guo * iort_pmsi_get_dev_id() - Get the device id for a device 460ae7c1838SHanjun Guo * @dev: The device for which the mapping is to be done. 461ae7c1838SHanjun Guo * @dev_id: The device ID found. 462ae7c1838SHanjun Guo * 463ae7c1838SHanjun Guo * Returns: 0 for successful find a dev id, -ENODEV on error 464ae7c1838SHanjun Guo */ 465ae7c1838SHanjun Guo int iort_pmsi_get_dev_id(struct device *dev, u32 *dev_id) 466ae7c1838SHanjun Guo { 467ae7c1838SHanjun Guo int i; 468ae7c1838SHanjun Guo struct acpi_iort_node *node; 469ae7c1838SHanjun Guo 470ae7c1838SHanjun Guo node = iort_find_dev_node(dev); 471ae7c1838SHanjun Guo if (!node) 472ae7c1838SHanjun Guo return -ENODEV; 473ae7c1838SHanjun Guo 474ae7c1838SHanjun Guo for (i = 0; i < node->mapping_count; i++) { 475ae7c1838SHanjun Guo if (iort_node_map_platform_id(node, dev_id, IORT_MSI_TYPE, i)) 476ae7c1838SHanjun Guo return 0; 477ae7c1838SHanjun Guo } 478ae7c1838SHanjun Guo 479ae7c1838SHanjun Guo return -ENODEV; 480ae7c1838SHanjun Guo } 481ae7c1838SHanjun Guo 482ae7c1838SHanjun Guo /** 4834bf2efd2STomasz Nowicki * iort_dev_find_its_id() - Find the ITS identifier for a device 4844bf2efd2STomasz Nowicki * @dev: The device. 4856cb6bf56SHanjun Guo * @req_id: Device's requester ID 4864bf2efd2STomasz Nowicki * @idx: Index of the ITS identifier list. 4874bf2efd2STomasz Nowicki * @its_id: ITS identifier. 4884bf2efd2STomasz Nowicki * 4894bf2efd2STomasz Nowicki * Returns: 0 on success, appropriate error value otherwise 4904bf2efd2STomasz Nowicki */ 4914bf2efd2STomasz Nowicki static int iort_dev_find_its_id(struct device *dev, u32 req_id, 4924bf2efd2STomasz Nowicki unsigned int idx, int *its_id) 4934bf2efd2STomasz Nowicki { 4944bf2efd2STomasz Nowicki struct acpi_iort_its_group *its; 4954bf2efd2STomasz Nowicki struct acpi_iort_node *node; 4964bf2efd2STomasz Nowicki 4974bf2efd2STomasz Nowicki node = iort_find_dev_node(dev); 4984bf2efd2STomasz Nowicki if (!node) 4994bf2efd2STomasz Nowicki return -ENXIO; 5004bf2efd2STomasz Nowicki 501697f6093SHanjun Guo node = iort_node_map_id(node, req_id, NULL, IORT_MSI_TYPE); 5024bf2efd2STomasz Nowicki if (!node) 5034bf2efd2STomasz Nowicki return -ENXIO; 5044bf2efd2STomasz Nowicki 5054bf2efd2STomasz Nowicki /* Move to ITS specific data */ 5064bf2efd2STomasz Nowicki its = (struct acpi_iort_its_group *)node->node_data; 5074bf2efd2STomasz Nowicki if (idx > its->its_count) { 5084bf2efd2STomasz Nowicki dev_err(dev, "requested ITS ID index [%d] is greater than available [%d]\n", 5094bf2efd2STomasz Nowicki idx, its->its_count); 5104bf2efd2STomasz Nowicki return -ENXIO; 5114bf2efd2STomasz Nowicki } 5124bf2efd2STomasz Nowicki 5134bf2efd2STomasz Nowicki *its_id = its->identifiers[idx]; 5144bf2efd2STomasz Nowicki return 0; 5154bf2efd2STomasz Nowicki } 5164bf2efd2STomasz Nowicki 5174bf2efd2STomasz Nowicki /** 5184bf2efd2STomasz Nowicki * iort_get_device_domain() - Find MSI domain related to a device 5194bf2efd2STomasz Nowicki * @dev: The device. 5204bf2efd2STomasz Nowicki * @req_id: Requester ID for the device. 5214bf2efd2STomasz Nowicki * 5224bf2efd2STomasz Nowicki * Returns: the MSI domain for this device, NULL otherwise 5234bf2efd2STomasz Nowicki */ 5244bf2efd2STomasz Nowicki struct irq_domain *iort_get_device_domain(struct device *dev, u32 req_id) 5254bf2efd2STomasz Nowicki { 5264bf2efd2STomasz Nowicki struct fwnode_handle *handle; 5274bf2efd2STomasz Nowicki int its_id; 5284bf2efd2STomasz Nowicki 5294bf2efd2STomasz Nowicki if (iort_dev_find_its_id(dev, req_id, 0, &its_id)) 5304bf2efd2STomasz Nowicki return NULL; 5314bf2efd2STomasz Nowicki 5324bf2efd2STomasz Nowicki handle = iort_find_domain_token(its_id); 5334bf2efd2STomasz Nowicki if (!handle) 5344bf2efd2STomasz Nowicki return NULL; 5354bf2efd2STomasz Nowicki 5364bf2efd2STomasz Nowicki return irq_find_matching_fwnode(handle, DOMAIN_BUS_PCI_MSI); 5374bf2efd2STomasz Nowicki } 5384bf2efd2STomasz Nowicki 539d4f54a18SHanjun Guo /** 540d4f54a18SHanjun Guo * iort_get_platform_device_domain() - Find MSI domain related to a 541d4f54a18SHanjun Guo * platform device 542d4f54a18SHanjun Guo * @dev: the dev pointer associated with the platform device 543d4f54a18SHanjun Guo * 544d4f54a18SHanjun Guo * Returns: the MSI domain for this device, NULL otherwise 545d4f54a18SHanjun Guo */ 546d4f54a18SHanjun Guo static struct irq_domain *iort_get_platform_device_domain(struct device *dev) 547d4f54a18SHanjun Guo { 548d4f54a18SHanjun Guo struct acpi_iort_node *node, *msi_parent; 549d4f54a18SHanjun Guo struct fwnode_handle *iort_fwnode; 550d4f54a18SHanjun Guo struct acpi_iort_its_group *its; 551d4f54a18SHanjun Guo int i; 552d4f54a18SHanjun Guo 553d4f54a18SHanjun Guo /* find its associated iort node */ 554d4f54a18SHanjun Guo node = iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT, 555d4f54a18SHanjun Guo iort_match_node_callback, dev); 556d4f54a18SHanjun Guo if (!node) 557d4f54a18SHanjun Guo return NULL; 558d4f54a18SHanjun Guo 559d4f54a18SHanjun Guo /* then find its msi parent node */ 560d4f54a18SHanjun Guo for (i = 0; i < node->mapping_count; i++) { 561d4f54a18SHanjun Guo msi_parent = iort_node_map_platform_id(node, NULL, 562d4f54a18SHanjun Guo IORT_MSI_TYPE, i); 563d4f54a18SHanjun Guo if (msi_parent) 564d4f54a18SHanjun Guo break; 565d4f54a18SHanjun Guo } 566d4f54a18SHanjun Guo 567d4f54a18SHanjun Guo if (!msi_parent) 568d4f54a18SHanjun Guo return NULL; 569d4f54a18SHanjun Guo 570d4f54a18SHanjun Guo /* Move to ITS specific data */ 571d4f54a18SHanjun Guo its = (struct acpi_iort_its_group *)msi_parent->node_data; 572d4f54a18SHanjun Guo 573d4f54a18SHanjun Guo iort_fwnode = iort_find_domain_token(its->identifiers[0]); 574d4f54a18SHanjun Guo if (!iort_fwnode) 575d4f54a18SHanjun Guo return NULL; 576d4f54a18SHanjun Guo 577d4f54a18SHanjun Guo return irq_find_matching_fwnode(iort_fwnode, DOMAIN_BUS_PLATFORM_MSI); 578d4f54a18SHanjun Guo } 579d4f54a18SHanjun Guo 580d4f54a18SHanjun Guo void acpi_configure_pmsi_domain(struct device *dev) 581d4f54a18SHanjun Guo { 582d4f54a18SHanjun Guo struct irq_domain *msi_domain; 583d4f54a18SHanjun Guo 584d4f54a18SHanjun Guo msi_domain = iort_get_platform_device_domain(dev); 585d4f54a18SHanjun Guo if (msi_domain) 586d4f54a18SHanjun Guo dev_set_msi_domain(dev, msi_domain); 587d4f54a18SHanjun Guo } 588d4f54a18SHanjun Guo 589bc8648d4SRobin Murphy static int __maybe_unused __get_pci_rid(struct pci_dev *pdev, u16 alias, 590bc8648d4SRobin Murphy void *data) 591643b8e4dSLorenzo Pieralisi { 592643b8e4dSLorenzo Pieralisi u32 *rid = data; 593643b8e4dSLorenzo Pieralisi 594643b8e4dSLorenzo Pieralisi *rid = alias; 595643b8e4dSLorenzo Pieralisi return 0; 596643b8e4dSLorenzo Pieralisi } 597643b8e4dSLorenzo Pieralisi 598643b8e4dSLorenzo Pieralisi static int arm_smmu_iort_xlate(struct device *dev, u32 streamid, 599643b8e4dSLorenzo Pieralisi struct fwnode_handle *fwnode, 600643b8e4dSLorenzo Pieralisi const struct iommu_ops *ops) 601643b8e4dSLorenzo Pieralisi { 602643b8e4dSLorenzo Pieralisi int ret = iommu_fwspec_init(dev, fwnode, ops); 603643b8e4dSLorenzo Pieralisi 604643b8e4dSLorenzo Pieralisi if (!ret) 605643b8e4dSLorenzo Pieralisi ret = iommu_fwspec_add_ids(dev, &streamid, 1); 606643b8e4dSLorenzo Pieralisi 607643b8e4dSLorenzo Pieralisi return ret; 608643b8e4dSLorenzo Pieralisi } 609643b8e4dSLorenzo Pieralisi 6101d9029d4SLorenzo Pieralisi static inline bool iort_iommu_driver_enabled(u8 type) 6111d9029d4SLorenzo Pieralisi { 6121d9029d4SLorenzo Pieralisi switch (type) { 6131d9029d4SLorenzo Pieralisi case ACPI_IORT_NODE_SMMU_V3: 6141d9029d4SLorenzo Pieralisi return IS_BUILTIN(CONFIG_ARM_SMMU_V3); 6151d9029d4SLorenzo Pieralisi case ACPI_IORT_NODE_SMMU: 6161d9029d4SLorenzo Pieralisi return IS_BUILTIN(CONFIG_ARM_SMMU); 6171d9029d4SLorenzo Pieralisi default: 6181d9029d4SLorenzo Pieralisi pr_warn("IORT node type %u does not describe an SMMU\n", type); 6191d9029d4SLorenzo Pieralisi return false; 6201d9029d4SLorenzo Pieralisi } 6211d9029d4SLorenzo Pieralisi } 6221d9029d4SLorenzo Pieralisi 623d49f2dedSLorenzo Pieralisi #ifdef CONFIG_IOMMU_API 624*e3d49392SLorenzo Pieralisi static inline const struct iommu_ops *iort_fwspec_iommu_ops( 625*e3d49392SLorenzo Pieralisi struct iommu_fwspec *fwspec) 626d49f2dedSLorenzo Pieralisi { 627d49f2dedSLorenzo Pieralisi return (fwspec && fwspec->ops) ? fwspec->ops : NULL; 628d49f2dedSLorenzo Pieralisi } 629d49f2dedSLorenzo Pieralisi 630*e3d49392SLorenzo Pieralisi static inline int iort_add_device_replay(const struct iommu_ops *ops, 631*e3d49392SLorenzo Pieralisi struct device *dev) 632d49f2dedSLorenzo Pieralisi { 633d49f2dedSLorenzo Pieralisi int err = 0; 634d49f2dedSLorenzo Pieralisi 635bc8648d4SRobin Murphy if (ops->add_device && dev->bus && !dev->iommu_group) 636d49f2dedSLorenzo Pieralisi err = ops->add_device(dev); 637d49f2dedSLorenzo Pieralisi 638d49f2dedSLorenzo Pieralisi return err; 639d49f2dedSLorenzo Pieralisi } 640d49f2dedSLorenzo Pieralisi #else 641*e3d49392SLorenzo Pieralisi static inline const struct iommu_ops *iort_fwspec_iommu_ops( 642*e3d49392SLorenzo Pieralisi struct iommu_fwspec *fwspec) 643d49f2dedSLorenzo Pieralisi { return NULL; } 644*e3d49392SLorenzo Pieralisi static inline int iort_add_device_replay(const struct iommu_ops *ops, 645*e3d49392SLorenzo Pieralisi struct device *dev) 646d49f2dedSLorenzo Pieralisi { return 0; } 647d49f2dedSLorenzo Pieralisi #endif 648d49f2dedSLorenzo Pieralisi 649bc8648d4SRobin Murphy static int iort_iommu_xlate(struct device *dev, struct acpi_iort_node *node, 650643b8e4dSLorenzo Pieralisi u32 streamid) 651643b8e4dSLorenzo Pieralisi { 652bc8648d4SRobin Murphy const struct iommu_ops *ops; 653643b8e4dSLorenzo Pieralisi struct fwnode_handle *iort_fwnode; 654643b8e4dSLorenzo Pieralisi 655bc8648d4SRobin Murphy if (!node) 656bc8648d4SRobin Murphy return -ENODEV; 657bc8648d4SRobin Murphy 658643b8e4dSLorenzo Pieralisi iort_fwnode = iort_get_fwnode(node); 659643b8e4dSLorenzo Pieralisi if (!iort_fwnode) 660bc8648d4SRobin Murphy return -ENODEV; 661643b8e4dSLorenzo Pieralisi 6625a1bb638SSricharan R /* 6635a1bb638SSricharan R * If the ops look-up fails, this means that either 6645a1bb638SSricharan R * the SMMU drivers have not been probed yet or that 6655a1bb638SSricharan R * the SMMU drivers are not built in the kernel; 6665a1bb638SSricharan R * Depending on whether the SMMU drivers are built-in 6675a1bb638SSricharan R * in the kernel or not, defer the IOMMU configuration 6685a1bb638SSricharan R * or just abort it. 6695a1bb638SSricharan R */ 670bc8648d4SRobin Murphy ops = iommu_ops_from_fwnode(iort_fwnode); 671643b8e4dSLorenzo Pieralisi if (!ops) 6725a1bb638SSricharan R return iort_iommu_driver_enabled(node->type) ? 673bc8648d4SRobin Murphy -EPROBE_DEFER : -ENODEV; 674643b8e4dSLorenzo Pieralisi 675bc8648d4SRobin Murphy return arm_smmu_iort_xlate(dev, streamid, iort_fwnode, ops); 676643b8e4dSLorenzo Pieralisi } 677643b8e4dSLorenzo Pieralisi 678bc8648d4SRobin Murphy struct iort_pci_alias_info { 679bc8648d4SRobin Murphy struct device *dev; 680bc8648d4SRobin Murphy struct acpi_iort_node *node; 681bc8648d4SRobin Murphy }; 682bc8648d4SRobin Murphy 683bc8648d4SRobin Murphy static int iort_pci_iommu_init(struct pci_dev *pdev, u16 alias, void *data) 684bc8648d4SRobin Murphy { 685bc8648d4SRobin Murphy struct iort_pci_alias_info *info = data; 686bc8648d4SRobin Murphy struct acpi_iort_node *parent; 687bc8648d4SRobin Murphy u32 streamid; 688bc8648d4SRobin Murphy 689bc8648d4SRobin Murphy parent = iort_node_map_id(info->node, alias, &streamid, 690bc8648d4SRobin Murphy IORT_IOMMU_TYPE); 691bc8648d4SRobin Murphy return iort_iommu_xlate(info->dev, parent, streamid); 692643b8e4dSLorenzo Pieralisi } 693643b8e4dSLorenzo Pieralisi 69410d8ab2cSLorenzo Pieralisi static int nc_dma_get_range(struct device *dev, u64 *size) 69510d8ab2cSLorenzo Pieralisi { 69610d8ab2cSLorenzo Pieralisi struct acpi_iort_node *node; 69710d8ab2cSLorenzo Pieralisi struct acpi_iort_named_component *ncomp; 69810d8ab2cSLorenzo Pieralisi 69910d8ab2cSLorenzo Pieralisi node = iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT, 70010d8ab2cSLorenzo Pieralisi iort_match_node_callback, dev); 70110d8ab2cSLorenzo Pieralisi if (!node) 70210d8ab2cSLorenzo Pieralisi return -ENODEV; 70310d8ab2cSLorenzo Pieralisi 70410d8ab2cSLorenzo Pieralisi ncomp = (struct acpi_iort_named_component *)node->node_data; 70510d8ab2cSLorenzo Pieralisi 70610d8ab2cSLorenzo Pieralisi *size = ncomp->memory_address_limit >= 64 ? U64_MAX : 70710d8ab2cSLorenzo Pieralisi 1ULL<<ncomp->memory_address_limit; 70810d8ab2cSLorenzo Pieralisi 70910d8ab2cSLorenzo Pieralisi return 0; 71010d8ab2cSLorenzo Pieralisi } 71110d8ab2cSLorenzo Pieralisi 712643b8e4dSLorenzo Pieralisi /** 7137ad42639SLorenzo Pieralisi * iort_dma_setup() - Set-up device DMA parameters. 71418b709beSLorenzo Pieralisi * 71518b709beSLorenzo Pieralisi * @dev: device to configure 7167ad42639SLorenzo Pieralisi * @dma_addr: device DMA address result pointer 7177ad42639SLorenzo Pieralisi * @size: DMA range size result pointer 71818b709beSLorenzo Pieralisi */ 7197ad42639SLorenzo Pieralisi void iort_dma_setup(struct device *dev, u64 *dma_addr, u64 *dma_size) 72018b709beSLorenzo Pieralisi { 7217ad42639SLorenzo Pieralisi u64 mask, dmaaddr = 0, size = 0, offset = 0; 7227ad42639SLorenzo Pieralisi int ret, msb; 7237ad42639SLorenzo Pieralisi 72418b709beSLorenzo Pieralisi /* 72518b709beSLorenzo Pieralisi * Set default coherent_dma_mask to 32 bit. Drivers are expected to 72618b709beSLorenzo Pieralisi * setup the correct supported mask. 72718b709beSLorenzo Pieralisi */ 72818b709beSLorenzo Pieralisi if (!dev->coherent_dma_mask) 72918b709beSLorenzo Pieralisi dev->coherent_dma_mask = DMA_BIT_MASK(32); 73018b709beSLorenzo Pieralisi 73118b709beSLorenzo Pieralisi /* 73218b709beSLorenzo Pieralisi * Set it to coherent_dma_mask by default if the architecture 73318b709beSLorenzo Pieralisi * code has not set it. 73418b709beSLorenzo Pieralisi */ 73518b709beSLorenzo Pieralisi if (!dev->dma_mask) 73618b709beSLorenzo Pieralisi dev->dma_mask = &dev->coherent_dma_mask; 7377ad42639SLorenzo Pieralisi 7387ad42639SLorenzo Pieralisi size = max(dev->coherent_dma_mask, dev->coherent_dma_mask + 1); 7397ad42639SLorenzo Pieralisi 74010d8ab2cSLorenzo Pieralisi if (dev_is_pci(dev)) 7417ad42639SLorenzo Pieralisi ret = acpi_dma_get_range(dev, &dmaaddr, &offset, &size); 74210d8ab2cSLorenzo Pieralisi else 74310d8ab2cSLorenzo Pieralisi ret = nc_dma_get_range(dev, &size); 74410d8ab2cSLorenzo Pieralisi 7457ad42639SLorenzo Pieralisi if (!ret) { 7467ad42639SLorenzo Pieralisi msb = fls64(dmaaddr + size - 1); 7477ad42639SLorenzo Pieralisi /* 7487ad42639SLorenzo Pieralisi * Round-up to the power-of-two mask or set 7497ad42639SLorenzo Pieralisi * the mask to the whole 64-bit address space 7507ad42639SLorenzo Pieralisi * in case the DMA region covers the full 7517ad42639SLorenzo Pieralisi * memory window. 7527ad42639SLorenzo Pieralisi */ 7537ad42639SLorenzo Pieralisi mask = msb == 64 ? U64_MAX : (1ULL << msb) - 1; 7547ad42639SLorenzo Pieralisi /* 7557ad42639SLorenzo Pieralisi * Limit coherent and dma mask based on size 7567ad42639SLorenzo Pieralisi * retrieved from firmware. 7577ad42639SLorenzo Pieralisi */ 7587ad42639SLorenzo Pieralisi dev->coherent_dma_mask = mask; 7597ad42639SLorenzo Pieralisi *dev->dma_mask = mask; 7607ad42639SLorenzo Pieralisi } 7617ad42639SLorenzo Pieralisi 7627ad42639SLorenzo Pieralisi *dma_addr = dmaaddr; 7637ad42639SLorenzo Pieralisi *dma_size = size; 7647ad42639SLorenzo Pieralisi 7657ad42639SLorenzo Pieralisi dev->dma_pfn_offset = PFN_DOWN(offset); 7667ad42639SLorenzo Pieralisi dev_dbg(dev, "dma_pfn_offset(%#08llx)\n", offset); 76718b709beSLorenzo Pieralisi } 76818b709beSLorenzo Pieralisi 76918b709beSLorenzo Pieralisi /** 770643b8e4dSLorenzo Pieralisi * iort_iommu_configure - Set-up IOMMU configuration for a device. 771643b8e4dSLorenzo Pieralisi * 772643b8e4dSLorenzo Pieralisi * @dev: device to configure 773643b8e4dSLorenzo Pieralisi * 774643b8e4dSLorenzo Pieralisi * Returns: iommu_ops pointer on configuration success 775643b8e4dSLorenzo Pieralisi * NULL on configuration failure 776643b8e4dSLorenzo Pieralisi */ 777643b8e4dSLorenzo Pieralisi const struct iommu_ops *iort_iommu_configure(struct device *dev) 778643b8e4dSLorenzo Pieralisi { 779643b8e4dSLorenzo Pieralisi struct acpi_iort_node *node, *parent; 780bc8648d4SRobin Murphy const struct iommu_ops *ops; 781643b8e4dSLorenzo Pieralisi u32 streamid = 0; 782bc8648d4SRobin Murphy int err = -ENODEV; 783643b8e4dSLorenzo Pieralisi 7844dac3210SLorenzo Pieralisi /* 7854dac3210SLorenzo Pieralisi * If we already translated the fwspec there 7864dac3210SLorenzo Pieralisi * is nothing left to do, return the iommu_ops. 7874dac3210SLorenzo Pieralisi */ 7884dac3210SLorenzo Pieralisi ops = iort_fwspec_iommu_ops(dev->iommu_fwspec); 7894dac3210SLorenzo Pieralisi if (ops) 7904dac3210SLorenzo Pieralisi return ops; 7914dac3210SLorenzo Pieralisi 792643b8e4dSLorenzo Pieralisi if (dev_is_pci(dev)) { 793643b8e4dSLorenzo Pieralisi struct pci_bus *bus = to_pci_dev(dev)->bus; 794bc8648d4SRobin Murphy struct iort_pci_alias_info info = { .dev = dev }; 795643b8e4dSLorenzo Pieralisi 796643b8e4dSLorenzo Pieralisi node = iort_scan_node(ACPI_IORT_NODE_PCI_ROOT_COMPLEX, 797643b8e4dSLorenzo Pieralisi iort_match_node_callback, &bus->dev); 798643b8e4dSLorenzo Pieralisi if (!node) 799643b8e4dSLorenzo Pieralisi return NULL; 800643b8e4dSLorenzo Pieralisi 801bc8648d4SRobin Murphy info.node = node; 802bc8648d4SRobin Murphy err = pci_for_each_dma_alias(to_pci_dev(dev), 803bc8648d4SRobin Murphy iort_pci_iommu_init, &info); 804643b8e4dSLorenzo Pieralisi } else { 805643b8e4dSLorenzo Pieralisi int i = 0; 806643b8e4dSLorenzo Pieralisi 807643b8e4dSLorenzo Pieralisi node = iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT, 808643b8e4dSLorenzo Pieralisi iort_match_node_callback, dev); 809643b8e4dSLorenzo Pieralisi if (!node) 810643b8e4dSLorenzo Pieralisi return NULL; 811643b8e4dSLorenzo Pieralisi 812bc8648d4SRobin Murphy do { 8138ca4f1d3SHanjun Guo parent = iort_node_map_platform_id(node, &streamid, 8148ca4f1d3SHanjun Guo IORT_IOMMU_TYPE, 8158ca4f1d3SHanjun Guo i++); 816bc8648d4SRobin Murphy 817bc8648d4SRobin Murphy if (parent) 818bc8648d4SRobin Murphy err = iort_iommu_xlate(dev, parent, streamid); 819bc8648d4SRobin Murphy } while (parent && !err); 820643b8e4dSLorenzo Pieralisi } 821643b8e4dSLorenzo Pieralisi 8225a1bb638SSricharan R /* 8235a1bb638SSricharan R * If we have reason to believe the IOMMU driver missed the initial 8245a1bb638SSricharan R * add_device callback for dev, replay it to get things in order. 8255a1bb638SSricharan R */ 826bc8648d4SRobin Murphy if (!err) { 8274d36037aSArnd Bergmann ops = iort_fwspec_iommu_ops(dev->iommu_fwspec); 828d49f2dedSLorenzo Pieralisi err = iort_add_device_replay(ops, dev); 829bc8648d4SRobin Murphy } 8305a1bb638SSricharan R 831058f8c3fSSricharan R /* Ignore all other errors apart from EPROBE_DEFER */ 832bc8648d4SRobin Murphy if (err == -EPROBE_DEFER) { 833bc8648d4SRobin Murphy ops = ERR_PTR(err); 834bc8648d4SRobin Murphy } else if (err) { 835bc8648d4SRobin Murphy dev_dbg(dev, "Adding to IOMMU failed: %d\n", err); 836058f8c3fSSricharan R ops = NULL; 837058f8c3fSSricharan R } 838058f8c3fSSricharan R 839643b8e4dSLorenzo Pieralisi return ops; 840643b8e4dSLorenzo Pieralisi } 841643b8e4dSLorenzo Pieralisi 842e4dadfa8SLorenzo Pieralisi static void __init acpi_iort_register_irq(int hwirq, const char *name, 843e4dadfa8SLorenzo Pieralisi int trigger, 844e4dadfa8SLorenzo Pieralisi struct resource *res) 845e4dadfa8SLorenzo Pieralisi { 846e4dadfa8SLorenzo Pieralisi int irq = acpi_register_gsi(NULL, hwirq, trigger, 847e4dadfa8SLorenzo Pieralisi ACPI_ACTIVE_HIGH); 848e4dadfa8SLorenzo Pieralisi 849e4dadfa8SLorenzo Pieralisi if (irq <= 0) { 850e4dadfa8SLorenzo Pieralisi pr_err("could not register gsi hwirq %d name [%s]\n", hwirq, 851e4dadfa8SLorenzo Pieralisi name); 852e4dadfa8SLorenzo Pieralisi return; 853e4dadfa8SLorenzo Pieralisi } 854e4dadfa8SLorenzo Pieralisi 855e4dadfa8SLorenzo Pieralisi res->start = irq; 856e4dadfa8SLorenzo Pieralisi res->end = irq; 857e4dadfa8SLorenzo Pieralisi res->flags = IORESOURCE_IRQ; 858e4dadfa8SLorenzo Pieralisi res->name = name; 859e4dadfa8SLorenzo Pieralisi } 860e4dadfa8SLorenzo Pieralisi 861e4dadfa8SLorenzo Pieralisi static int __init arm_smmu_v3_count_resources(struct acpi_iort_node *node) 862e4dadfa8SLorenzo Pieralisi { 863e4dadfa8SLorenzo Pieralisi struct acpi_iort_smmu_v3 *smmu; 864e4dadfa8SLorenzo Pieralisi /* Always present mem resource */ 865e4dadfa8SLorenzo Pieralisi int num_res = 1; 866e4dadfa8SLorenzo Pieralisi 867e4dadfa8SLorenzo Pieralisi /* Retrieve SMMUv3 specific data */ 868e4dadfa8SLorenzo Pieralisi smmu = (struct acpi_iort_smmu_v3 *)node->node_data; 869e4dadfa8SLorenzo Pieralisi 870e4dadfa8SLorenzo Pieralisi if (smmu->event_gsiv) 871e4dadfa8SLorenzo Pieralisi num_res++; 872e4dadfa8SLorenzo Pieralisi 873e4dadfa8SLorenzo Pieralisi if (smmu->pri_gsiv) 874e4dadfa8SLorenzo Pieralisi num_res++; 875e4dadfa8SLorenzo Pieralisi 876e4dadfa8SLorenzo Pieralisi if (smmu->gerr_gsiv) 877e4dadfa8SLorenzo Pieralisi num_res++; 878e4dadfa8SLorenzo Pieralisi 879e4dadfa8SLorenzo Pieralisi if (smmu->sync_gsiv) 880e4dadfa8SLorenzo Pieralisi num_res++; 881e4dadfa8SLorenzo Pieralisi 882e4dadfa8SLorenzo Pieralisi return num_res; 883e4dadfa8SLorenzo Pieralisi } 884e4dadfa8SLorenzo Pieralisi 885f935448aSGeetha Sowjanya static bool arm_smmu_v3_is_combined_irq(struct acpi_iort_smmu_v3 *smmu) 886f935448aSGeetha Sowjanya { 887f935448aSGeetha Sowjanya /* 888f935448aSGeetha Sowjanya * Cavium ThunderX2 implementation doesn't not support unique 889f935448aSGeetha Sowjanya * irq line. Use single irq line for all the SMMUv3 interrupts. 890f935448aSGeetha Sowjanya */ 891f935448aSGeetha Sowjanya if (smmu->model != ACPI_IORT_SMMU_V3_CAVIUM_CN99XX) 892f935448aSGeetha Sowjanya return false; 893f935448aSGeetha Sowjanya 894f935448aSGeetha Sowjanya /* 895f935448aSGeetha Sowjanya * ThunderX2 doesn't support MSIs from the SMMU, so we're checking 896f935448aSGeetha Sowjanya * SPI numbers here. 897f935448aSGeetha Sowjanya */ 898f935448aSGeetha Sowjanya return smmu->event_gsiv == smmu->pri_gsiv && 899f935448aSGeetha Sowjanya smmu->event_gsiv == smmu->gerr_gsiv && 900f935448aSGeetha Sowjanya smmu->event_gsiv == smmu->sync_gsiv; 901f935448aSGeetha Sowjanya } 902f935448aSGeetha Sowjanya 903403e8c7cSLinu Cherian static unsigned long arm_smmu_v3_resource_size(struct acpi_iort_smmu_v3 *smmu) 904403e8c7cSLinu Cherian { 905403e8c7cSLinu Cherian /* 906403e8c7cSLinu Cherian * Override the size, for Cavium ThunderX2 implementation 907403e8c7cSLinu Cherian * which doesn't support the page 1 SMMU register space. 908403e8c7cSLinu Cherian */ 909403e8c7cSLinu Cherian if (smmu->model == ACPI_IORT_SMMU_V3_CAVIUM_CN99XX) 910403e8c7cSLinu Cherian return SZ_64K; 911403e8c7cSLinu Cherian 912403e8c7cSLinu Cherian return SZ_128K; 913403e8c7cSLinu Cherian } 914403e8c7cSLinu Cherian 915e4dadfa8SLorenzo Pieralisi static void __init arm_smmu_v3_init_resources(struct resource *res, 916e4dadfa8SLorenzo Pieralisi struct acpi_iort_node *node) 917e4dadfa8SLorenzo Pieralisi { 918e4dadfa8SLorenzo Pieralisi struct acpi_iort_smmu_v3 *smmu; 919e4dadfa8SLorenzo Pieralisi int num_res = 0; 920e4dadfa8SLorenzo Pieralisi 921e4dadfa8SLorenzo Pieralisi /* Retrieve SMMUv3 specific data */ 922e4dadfa8SLorenzo Pieralisi smmu = (struct acpi_iort_smmu_v3 *)node->node_data; 923e4dadfa8SLorenzo Pieralisi 924e4dadfa8SLorenzo Pieralisi res[num_res].start = smmu->base_address; 925403e8c7cSLinu Cherian res[num_res].end = smmu->base_address + 926403e8c7cSLinu Cherian arm_smmu_v3_resource_size(smmu) - 1; 927e4dadfa8SLorenzo Pieralisi res[num_res].flags = IORESOURCE_MEM; 928e4dadfa8SLorenzo Pieralisi 929e4dadfa8SLorenzo Pieralisi num_res++; 930f935448aSGeetha Sowjanya if (arm_smmu_v3_is_combined_irq(smmu)) { 931f935448aSGeetha Sowjanya if (smmu->event_gsiv) 932f935448aSGeetha Sowjanya acpi_iort_register_irq(smmu->event_gsiv, "combined", 933f935448aSGeetha Sowjanya ACPI_EDGE_SENSITIVE, 934f935448aSGeetha Sowjanya &res[num_res++]); 935f935448aSGeetha Sowjanya } else { 936e4dadfa8SLorenzo Pieralisi 937e4dadfa8SLorenzo Pieralisi if (smmu->event_gsiv) 938e4dadfa8SLorenzo Pieralisi acpi_iort_register_irq(smmu->event_gsiv, "eventq", 939e4dadfa8SLorenzo Pieralisi ACPI_EDGE_SENSITIVE, 940e4dadfa8SLorenzo Pieralisi &res[num_res++]); 941e4dadfa8SLorenzo Pieralisi 942e4dadfa8SLorenzo Pieralisi if (smmu->pri_gsiv) 943e4dadfa8SLorenzo Pieralisi acpi_iort_register_irq(smmu->pri_gsiv, "priq", 944e4dadfa8SLorenzo Pieralisi ACPI_EDGE_SENSITIVE, 945e4dadfa8SLorenzo Pieralisi &res[num_res++]); 946e4dadfa8SLorenzo Pieralisi 947e4dadfa8SLorenzo Pieralisi if (smmu->gerr_gsiv) 948e4dadfa8SLorenzo Pieralisi acpi_iort_register_irq(smmu->gerr_gsiv, "gerror", 949e4dadfa8SLorenzo Pieralisi ACPI_EDGE_SENSITIVE, 950e4dadfa8SLorenzo Pieralisi &res[num_res++]); 951e4dadfa8SLorenzo Pieralisi 952e4dadfa8SLorenzo Pieralisi if (smmu->sync_gsiv) 953e4dadfa8SLorenzo Pieralisi acpi_iort_register_irq(smmu->sync_gsiv, "cmdq-sync", 954e4dadfa8SLorenzo Pieralisi ACPI_EDGE_SENSITIVE, 955e4dadfa8SLorenzo Pieralisi &res[num_res++]); 956e4dadfa8SLorenzo Pieralisi } 957f935448aSGeetha Sowjanya } 958e4dadfa8SLorenzo Pieralisi 959e4dadfa8SLorenzo Pieralisi static bool __init arm_smmu_v3_is_coherent(struct acpi_iort_node *node) 960e4dadfa8SLorenzo Pieralisi { 961e4dadfa8SLorenzo Pieralisi struct acpi_iort_smmu_v3 *smmu; 962e4dadfa8SLorenzo Pieralisi 963e4dadfa8SLorenzo Pieralisi /* Retrieve SMMUv3 specific data */ 964e4dadfa8SLorenzo Pieralisi smmu = (struct acpi_iort_smmu_v3 *)node->node_data; 965e4dadfa8SLorenzo Pieralisi 966e4dadfa8SLorenzo Pieralisi return smmu->flags & ACPI_IORT_SMMU_V3_COHACC_OVERRIDE; 967e4dadfa8SLorenzo Pieralisi } 968e4dadfa8SLorenzo Pieralisi 96975808131SLorenzo Pieralisi #if defined(CONFIG_ACPI_NUMA) 9705fe0ce3bSGanapatrao Kulkarni /* 9715fe0ce3bSGanapatrao Kulkarni * set numa proximity domain for smmuv3 device 9725fe0ce3bSGanapatrao Kulkarni */ 9735fe0ce3bSGanapatrao Kulkarni static void __init arm_smmu_v3_set_proximity(struct device *dev, 9745fe0ce3bSGanapatrao Kulkarni struct acpi_iort_node *node) 9755fe0ce3bSGanapatrao Kulkarni { 9765fe0ce3bSGanapatrao Kulkarni struct acpi_iort_smmu_v3 *smmu; 9775fe0ce3bSGanapatrao Kulkarni 9785fe0ce3bSGanapatrao Kulkarni smmu = (struct acpi_iort_smmu_v3 *)node->node_data; 9795fe0ce3bSGanapatrao Kulkarni if (smmu->flags & ACPI_IORT_SMMU_V3_PXM_VALID) { 9805fe0ce3bSGanapatrao Kulkarni set_dev_node(dev, acpi_map_pxm_to_node(smmu->pxm)); 9815fe0ce3bSGanapatrao Kulkarni pr_info("SMMU-v3[%llx] Mapped to Proximity domain %d\n", 9825fe0ce3bSGanapatrao Kulkarni smmu->base_address, 9835fe0ce3bSGanapatrao Kulkarni smmu->pxm); 9845fe0ce3bSGanapatrao Kulkarni } 9855fe0ce3bSGanapatrao Kulkarni } 9865fe0ce3bSGanapatrao Kulkarni #else 9875fe0ce3bSGanapatrao Kulkarni #define arm_smmu_v3_set_proximity NULL 9885fe0ce3bSGanapatrao Kulkarni #endif 9895fe0ce3bSGanapatrao Kulkarni 990d6fcd3b1SLorenzo Pieralisi static int __init arm_smmu_count_resources(struct acpi_iort_node *node) 991d6fcd3b1SLorenzo Pieralisi { 992d6fcd3b1SLorenzo Pieralisi struct acpi_iort_smmu *smmu; 993d6fcd3b1SLorenzo Pieralisi 994d6fcd3b1SLorenzo Pieralisi /* Retrieve SMMU specific data */ 995d6fcd3b1SLorenzo Pieralisi smmu = (struct acpi_iort_smmu *)node->node_data; 996d6fcd3b1SLorenzo Pieralisi 997d6fcd3b1SLorenzo Pieralisi /* 998d6fcd3b1SLorenzo Pieralisi * Only consider the global fault interrupt and ignore the 999d6fcd3b1SLorenzo Pieralisi * configuration access interrupt. 1000d6fcd3b1SLorenzo Pieralisi * 1001d6fcd3b1SLorenzo Pieralisi * MMIO address and global fault interrupt resources are always 1002d6fcd3b1SLorenzo Pieralisi * present so add them to the context interrupt count as a static 1003d6fcd3b1SLorenzo Pieralisi * value. 1004d6fcd3b1SLorenzo Pieralisi */ 1005d6fcd3b1SLorenzo Pieralisi return smmu->context_interrupt_count + 2; 1006d6fcd3b1SLorenzo Pieralisi } 1007d6fcd3b1SLorenzo Pieralisi 1008d6fcd3b1SLorenzo Pieralisi static void __init arm_smmu_init_resources(struct resource *res, 1009d6fcd3b1SLorenzo Pieralisi struct acpi_iort_node *node) 1010d6fcd3b1SLorenzo Pieralisi { 1011d6fcd3b1SLorenzo Pieralisi struct acpi_iort_smmu *smmu; 1012d6fcd3b1SLorenzo Pieralisi int i, hw_irq, trigger, num_res = 0; 1013d6fcd3b1SLorenzo Pieralisi u64 *ctx_irq, *glb_irq; 1014d6fcd3b1SLorenzo Pieralisi 1015d6fcd3b1SLorenzo Pieralisi /* Retrieve SMMU specific data */ 1016d6fcd3b1SLorenzo Pieralisi smmu = (struct acpi_iort_smmu *)node->node_data; 1017d6fcd3b1SLorenzo Pieralisi 1018d6fcd3b1SLorenzo Pieralisi res[num_res].start = smmu->base_address; 1019d6fcd3b1SLorenzo Pieralisi res[num_res].end = smmu->base_address + smmu->span - 1; 1020d6fcd3b1SLorenzo Pieralisi res[num_res].flags = IORESOURCE_MEM; 1021d6fcd3b1SLorenzo Pieralisi num_res++; 1022d6fcd3b1SLorenzo Pieralisi 1023d6fcd3b1SLorenzo Pieralisi glb_irq = ACPI_ADD_PTR(u64, node, smmu->global_interrupt_offset); 1024d6fcd3b1SLorenzo Pieralisi /* Global IRQs */ 1025d6fcd3b1SLorenzo Pieralisi hw_irq = IORT_IRQ_MASK(glb_irq[0]); 1026d6fcd3b1SLorenzo Pieralisi trigger = IORT_IRQ_TRIGGER_MASK(glb_irq[0]); 1027d6fcd3b1SLorenzo Pieralisi 1028d6fcd3b1SLorenzo Pieralisi acpi_iort_register_irq(hw_irq, "arm-smmu-global", trigger, 1029d6fcd3b1SLorenzo Pieralisi &res[num_res++]); 1030d6fcd3b1SLorenzo Pieralisi 1031d6fcd3b1SLorenzo Pieralisi /* Context IRQs */ 1032d6fcd3b1SLorenzo Pieralisi ctx_irq = ACPI_ADD_PTR(u64, node, smmu->context_interrupt_offset); 1033d6fcd3b1SLorenzo Pieralisi for (i = 0; i < smmu->context_interrupt_count; i++) { 1034d6fcd3b1SLorenzo Pieralisi hw_irq = IORT_IRQ_MASK(ctx_irq[i]); 1035d6fcd3b1SLorenzo Pieralisi trigger = IORT_IRQ_TRIGGER_MASK(ctx_irq[i]); 1036d6fcd3b1SLorenzo Pieralisi 1037d6fcd3b1SLorenzo Pieralisi acpi_iort_register_irq(hw_irq, "arm-smmu-context", trigger, 1038d6fcd3b1SLorenzo Pieralisi &res[num_res++]); 1039d6fcd3b1SLorenzo Pieralisi } 1040d6fcd3b1SLorenzo Pieralisi } 1041d6fcd3b1SLorenzo Pieralisi 1042d6fcd3b1SLorenzo Pieralisi static bool __init arm_smmu_is_coherent(struct acpi_iort_node *node) 1043d6fcd3b1SLorenzo Pieralisi { 1044d6fcd3b1SLorenzo Pieralisi struct acpi_iort_smmu *smmu; 1045d6fcd3b1SLorenzo Pieralisi 1046d6fcd3b1SLorenzo Pieralisi /* Retrieve SMMU specific data */ 1047d6fcd3b1SLorenzo Pieralisi smmu = (struct acpi_iort_smmu *)node->node_data; 1048d6fcd3b1SLorenzo Pieralisi 1049d6fcd3b1SLorenzo Pieralisi return smmu->flags & ACPI_IORT_SMMU_COHERENT_WALK; 1050d6fcd3b1SLorenzo Pieralisi } 1051d6fcd3b1SLorenzo Pieralisi 1052846f0e9eSLorenzo Pieralisi struct iort_iommu_config { 1053846f0e9eSLorenzo Pieralisi const char *name; 1054846f0e9eSLorenzo Pieralisi int (*iommu_init)(struct acpi_iort_node *node); 1055846f0e9eSLorenzo Pieralisi bool (*iommu_is_coherent)(struct acpi_iort_node *node); 1056846f0e9eSLorenzo Pieralisi int (*iommu_count_resources)(struct acpi_iort_node *node); 1057846f0e9eSLorenzo Pieralisi void (*iommu_init_resources)(struct resource *res, 1058846f0e9eSLorenzo Pieralisi struct acpi_iort_node *node); 10595fe0ce3bSGanapatrao Kulkarni void (*iommu_set_proximity)(struct device *dev, 10605fe0ce3bSGanapatrao Kulkarni struct acpi_iort_node *node); 1061846f0e9eSLorenzo Pieralisi }; 1062846f0e9eSLorenzo Pieralisi 1063e4dadfa8SLorenzo Pieralisi static const struct iort_iommu_config iort_arm_smmu_v3_cfg __initconst = { 1064e4dadfa8SLorenzo Pieralisi .name = "arm-smmu-v3", 1065e4dadfa8SLorenzo Pieralisi .iommu_is_coherent = arm_smmu_v3_is_coherent, 1066e4dadfa8SLorenzo Pieralisi .iommu_count_resources = arm_smmu_v3_count_resources, 10675fe0ce3bSGanapatrao Kulkarni .iommu_init_resources = arm_smmu_v3_init_resources, 10685fe0ce3bSGanapatrao Kulkarni .iommu_set_proximity = arm_smmu_v3_set_proximity, 1069e4dadfa8SLorenzo Pieralisi }; 1070e4dadfa8SLorenzo Pieralisi 1071d6fcd3b1SLorenzo Pieralisi static const struct iort_iommu_config iort_arm_smmu_cfg __initconst = { 1072d6fcd3b1SLorenzo Pieralisi .name = "arm-smmu", 1073d6fcd3b1SLorenzo Pieralisi .iommu_is_coherent = arm_smmu_is_coherent, 1074d6fcd3b1SLorenzo Pieralisi .iommu_count_resources = arm_smmu_count_resources, 1075d6fcd3b1SLorenzo Pieralisi .iommu_init_resources = arm_smmu_init_resources 1076d6fcd3b1SLorenzo Pieralisi }; 1077d6fcd3b1SLorenzo Pieralisi 1078*e3d49392SLorenzo Pieralisi static __init const struct iort_iommu_config *iort_get_iommu_cfg( 1079*e3d49392SLorenzo Pieralisi struct acpi_iort_node *node) 1080846f0e9eSLorenzo Pieralisi { 1081e4dadfa8SLorenzo Pieralisi switch (node->type) { 1082e4dadfa8SLorenzo Pieralisi case ACPI_IORT_NODE_SMMU_V3: 1083e4dadfa8SLorenzo Pieralisi return &iort_arm_smmu_v3_cfg; 1084d6fcd3b1SLorenzo Pieralisi case ACPI_IORT_NODE_SMMU: 1085d6fcd3b1SLorenzo Pieralisi return &iort_arm_smmu_cfg; 1086e4dadfa8SLorenzo Pieralisi default: 1087846f0e9eSLorenzo Pieralisi return NULL; 1088846f0e9eSLorenzo Pieralisi } 1089e4dadfa8SLorenzo Pieralisi } 1090846f0e9eSLorenzo Pieralisi 1091846f0e9eSLorenzo Pieralisi /** 1092846f0e9eSLorenzo Pieralisi * iort_add_smmu_platform_device() - Allocate a platform device for SMMU 1093846f0e9eSLorenzo Pieralisi * @node: Pointer to SMMU ACPI IORT node 1094846f0e9eSLorenzo Pieralisi * 1095846f0e9eSLorenzo Pieralisi * Returns: 0 on success, <0 failure 1096846f0e9eSLorenzo Pieralisi */ 1097846f0e9eSLorenzo Pieralisi static int __init iort_add_smmu_platform_device(struct acpi_iort_node *node) 1098846f0e9eSLorenzo Pieralisi { 1099846f0e9eSLorenzo Pieralisi struct fwnode_handle *fwnode; 1100846f0e9eSLorenzo Pieralisi struct platform_device *pdev; 1101846f0e9eSLorenzo Pieralisi struct resource *r; 1102846f0e9eSLorenzo Pieralisi enum dev_dma_attr attr; 1103846f0e9eSLorenzo Pieralisi int ret, count; 1104846f0e9eSLorenzo Pieralisi const struct iort_iommu_config *ops = iort_get_iommu_cfg(node); 1105846f0e9eSLorenzo Pieralisi 1106846f0e9eSLorenzo Pieralisi if (!ops) 1107846f0e9eSLorenzo Pieralisi return -ENODEV; 1108846f0e9eSLorenzo Pieralisi 1109846f0e9eSLorenzo Pieralisi pdev = platform_device_alloc(ops->name, PLATFORM_DEVID_AUTO); 1110846f0e9eSLorenzo Pieralisi if (!pdev) 11115e5afa6cSDan Carpenter return -ENOMEM; 1112846f0e9eSLorenzo Pieralisi 11135fe0ce3bSGanapatrao Kulkarni if (ops->iommu_set_proximity) 11145fe0ce3bSGanapatrao Kulkarni ops->iommu_set_proximity(&pdev->dev, node); 11155fe0ce3bSGanapatrao Kulkarni 1116846f0e9eSLorenzo Pieralisi count = ops->iommu_count_resources(node); 1117846f0e9eSLorenzo Pieralisi 1118846f0e9eSLorenzo Pieralisi r = kcalloc(count, sizeof(*r), GFP_KERNEL); 1119846f0e9eSLorenzo Pieralisi if (!r) { 1120846f0e9eSLorenzo Pieralisi ret = -ENOMEM; 1121846f0e9eSLorenzo Pieralisi goto dev_put; 1122846f0e9eSLorenzo Pieralisi } 1123846f0e9eSLorenzo Pieralisi 1124846f0e9eSLorenzo Pieralisi ops->iommu_init_resources(r, node); 1125846f0e9eSLorenzo Pieralisi 1126846f0e9eSLorenzo Pieralisi ret = platform_device_add_resources(pdev, r, count); 1127846f0e9eSLorenzo Pieralisi /* 1128846f0e9eSLorenzo Pieralisi * Resources are duplicated in platform_device_add_resources, 1129846f0e9eSLorenzo Pieralisi * free their allocated memory 1130846f0e9eSLorenzo Pieralisi */ 1131846f0e9eSLorenzo Pieralisi kfree(r); 1132846f0e9eSLorenzo Pieralisi 1133846f0e9eSLorenzo Pieralisi if (ret) 1134846f0e9eSLorenzo Pieralisi goto dev_put; 1135846f0e9eSLorenzo Pieralisi 1136846f0e9eSLorenzo Pieralisi /* 1137846f0e9eSLorenzo Pieralisi * Add a copy of IORT node pointer to platform_data to 1138846f0e9eSLorenzo Pieralisi * be used to retrieve IORT data information. 1139846f0e9eSLorenzo Pieralisi */ 1140846f0e9eSLorenzo Pieralisi ret = platform_device_add_data(pdev, &node, sizeof(node)); 1141846f0e9eSLorenzo Pieralisi if (ret) 1142846f0e9eSLorenzo Pieralisi goto dev_put; 1143846f0e9eSLorenzo Pieralisi 1144846f0e9eSLorenzo Pieralisi /* 1145846f0e9eSLorenzo Pieralisi * We expect the dma masks to be equivalent for 1146846f0e9eSLorenzo Pieralisi * all SMMUs set-ups 1147846f0e9eSLorenzo Pieralisi */ 1148846f0e9eSLorenzo Pieralisi pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask; 1149846f0e9eSLorenzo Pieralisi 1150846f0e9eSLorenzo Pieralisi fwnode = iort_get_fwnode(node); 1151846f0e9eSLorenzo Pieralisi 1152846f0e9eSLorenzo Pieralisi if (!fwnode) { 1153846f0e9eSLorenzo Pieralisi ret = -ENODEV; 1154846f0e9eSLorenzo Pieralisi goto dev_put; 1155846f0e9eSLorenzo Pieralisi } 1156846f0e9eSLorenzo Pieralisi 1157846f0e9eSLorenzo Pieralisi pdev->dev.fwnode = fwnode; 1158846f0e9eSLorenzo Pieralisi 1159846f0e9eSLorenzo Pieralisi attr = ops->iommu_is_coherent(node) ? 1160846f0e9eSLorenzo Pieralisi DEV_DMA_COHERENT : DEV_DMA_NON_COHERENT; 1161846f0e9eSLorenzo Pieralisi 1162846f0e9eSLorenzo Pieralisi /* Configure DMA for the page table walker */ 1163846f0e9eSLorenzo Pieralisi acpi_dma_configure(&pdev->dev, attr); 1164846f0e9eSLorenzo Pieralisi 1165846f0e9eSLorenzo Pieralisi ret = platform_device_add(pdev); 1166846f0e9eSLorenzo Pieralisi if (ret) 1167846f0e9eSLorenzo Pieralisi goto dma_deconfigure; 1168846f0e9eSLorenzo Pieralisi 1169846f0e9eSLorenzo Pieralisi return 0; 1170846f0e9eSLorenzo Pieralisi 1171846f0e9eSLorenzo Pieralisi dma_deconfigure: 1172846f0e9eSLorenzo Pieralisi acpi_dma_deconfigure(&pdev->dev); 1173846f0e9eSLorenzo Pieralisi dev_put: 1174846f0e9eSLorenzo Pieralisi platform_device_put(pdev); 1175846f0e9eSLorenzo Pieralisi 1176846f0e9eSLorenzo Pieralisi return ret; 1177846f0e9eSLorenzo Pieralisi } 1178846f0e9eSLorenzo Pieralisi 1179846f0e9eSLorenzo Pieralisi static void __init iort_init_platform_devices(void) 1180846f0e9eSLorenzo Pieralisi { 1181846f0e9eSLorenzo Pieralisi struct acpi_iort_node *iort_node, *iort_end; 1182846f0e9eSLorenzo Pieralisi struct acpi_table_iort *iort; 1183846f0e9eSLorenzo Pieralisi struct fwnode_handle *fwnode; 1184846f0e9eSLorenzo Pieralisi int i, ret; 1185846f0e9eSLorenzo Pieralisi 1186846f0e9eSLorenzo Pieralisi /* 1187846f0e9eSLorenzo Pieralisi * iort_table and iort both point to the start of IORT table, but 1188846f0e9eSLorenzo Pieralisi * have different struct types 1189846f0e9eSLorenzo Pieralisi */ 1190846f0e9eSLorenzo Pieralisi iort = (struct acpi_table_iort *)iort_table; 1191846f0e9eSLorenzo Pieralisi 1192846f0e9eSLorenzo Pieralisi /* Get the first IORT node */ 1193846f0e9eSLorenzo Pieralisi iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort, 1194846f0e9eSLorenzo Pieralisi iort->node_offset); 1195846f0e9eSLorenzo Pieralisi iort_end = ACPI_ADD_PTR(struct acpi_iort_node, iort, 1196846f0e9eSLorenzo Pieralisi iort_table->length); 1197846f0e9eSLorenzo Pieralisi 1198846f0e9eSLorenzo Pieralisi for (i = 0; i < iort->node_count; i++) { 1199846f0e9eSLorenzo Pieralisi if (iort_node >= iort_end) { 1200846f0e9eSLorenzo Pieralisi pr_err("iort node pointer overflows, bad table\n"); 1201846f0e9eSLorenzo Pieralisi return; 1202846f0e9eSLorenzo Pieralisi } 1203846f0e9eSLorenzo Pieralisi 1204846f0e9eSLorenzo Pieralisi if ((iort_node->type == ACPI_IORT_NODE_SMMU) || 1205846f0e9eSLorenzo Pieralisi (iort_node->type == ACPI_IORT_NODE_SMMU_V3)) { 1206846f0e9eSLorenzo Pieralisi 1207846f0e9eSLorenzo Pieralisi fwnode = acpi_alloc_fwnode_static(); 1208846f0e9eSLorenzo Pieralisi if (!fwnode) 1209846f0e9eSLorenzo Pieralisi return; 1210846f0e9eSLorenzo Pieralisi 1211846f0e9eSLorenzo Pieralisi iort_set_fwnode(iort_node, fwnode); 1212846f0e9eSLorenzo Pieralisi 1213846f0e9eSLorenzo Pieralisi ret = iort_add_smmu_platform_device(iort_node); 1214846f0e9eSLorenzo Pieralisi if (ret) { 1215846f0e9eSLorenzo Pieralisi iort_delete_fwnode(iort_node); 1216846f0e9eSLorenzo Pieralisi acpi_free_fwnode_static(fwnode); 1217846f0e9eSLorenzo Pieralisi return; 1218846f0e9eSLorenzo Pieralisi } 1219846f0e9eSLorenzo Pieralisi } 1220846f0e9eSLorenzo Pieralisi 1221846f0e9eSLorenzo Pieralisi iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort_node, 1222846f0e9eSLorenzo Pieralisi iort_node->length); 1223846f0e9eSLorenzo Pieralisi } 1224846f0e9eSLorenzo Pieralisi } 1225846f0e9eSLorenzo Pieralisi 122688ef16d8STomasz Nowicki void __init acpi_iort_init(void) 122788ef16d8STomasz Nowicki { 122888ef16d8STomasz Nowicki acpi_status status; 122988ef16d8STomasz Nowicki 123088ef16d8STomasz Nowicki status = acpi_get_table(ACPI_SIG_IORT, 0, &iort_table); 123134ceea27SLorenzo Pieralisi if (ACPI_FAILURE(status)) { 123234ceea27SLorenzo Pieralisi if (status != AE_NOT_FOUND) { 123388ef16d8STomasz Nowicki const char *msg = acpi_format_exception(status); 123434ceea27SLorenzo Pieralisi 123588ef16d8STomasz Nowicki pr_err("Failed to get table, %s\n", msg); 123688ef16d8STomasz Nowicki } 123734ceea27SLorenzo Pieralisi 123834ceea27SLorenzo Pieralisi return; 123934ceea27SLorenzo Pieralisi } 124034ceea27SLorenzo Pieralisi 1241846f0e9eSLorenzo Pieralisi iort_init_platform_devices(); 124288ef16d8STomasz Nowicki } 1243