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 */ 917936df92SLorenzo Pieralisi static inline 927936df92SLorenzo Pieralisi struct fwnode_handle *iort_get_fwnode(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 309618f535aSLorenzo Pieralisi static 310618f535aSLorenzo Pieralisi struct acpi_iort_node *iort_node_get_id(struct acpi_iort_node *node, 3118ca4f1d3SHanjun Guo u32 *id_out, int index) 312618f535aSLorenzo Pieralisi { 313618f535aSLorenzo Pieralisi struct acpi_iort_node *parent; 314618f535aSLorenzo Pieralisi struct acpi_iort_id_mapping *map; 315618f535aSLorenzo Pieralisi 316618f535aSLorenzo Pieralisi if (!node->mapping_offset || !node->mapping_count || 317618f535aSLorenzo Pieralisi index >= node->mapping_count) 318618f535aSLorenzo Pieralisi return NULL; 319618f535aSLorenzo Pieralisi 320618f535aSLorenzo Pieralisi map = ACPI_ADD_PTR(struct acpi_iort_id_mapping, node, 321030abd8aSLorenzo Pieralisi node->mapping_offset + index * sizeof(*map)); 322618f535aSLorenzo Pieralisi 323618f535aSLorenzo Pieralisi /* Firmware bug! */ 324618f535aSLorenzo Pieralisi if (!map->output_reference) { 325618f535aSLorenzo Pieralisi pr_err(FW_BUG "[node %p type %d] ID map has NULL parent reference\n", 326618f535aSLorenzo Pieralisi node, node->type); 327618f535aSLorenzo Pieralisi return NULL; 328618f535aSLorenzo Pieralisi } 329618f535aSLorenzo Pieralisi 330618f535aSLorenzo Pieralisi parent = ACPI_ADD_PTR(struct acpi_iort_node, iort_table, 331618f535aSLorenzo Pieralisi map->output_reference); 332618f535aSLorenzo Pieralisi 333030abd8aSLorenzo Pieralisi if (map->flags & ACPI_IORT_ID_SINGLE_MAPPING) { 334618f535aSLorenzo Pieralisi if (node->type == ACPI_IORT_NODE_NAMED_COMPONENT || 335618f535aSLorenzo Pieralisi node->type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX) { 336030abd8aSLorenzo Pieralisi *id_out = map->output_base; 337618f535aSLorenzo Pieralisi return parent; 338618f535aSLorenzo Pieralisi } 339618f535aSLorenzo Pieralisi } 340618f535aSLorenzo Pieralisi 341618f535aSLorenzo Pieralisi return NULL; 342618f535aSLorenzo Pieralisi } 343618f535aSLorenzo Pieralisi 344697f6093SHanjun Guo static struct acpi_iort_node *iort_node_map_id(struct acpi_iort_node *node, 345697f6093SHanjun Guo u32 id_in, u32 *id_out, 346ea50b524SLorenzo Pieralisi u8 type_mask) 34788ef16d8STomasz Nowicki { 348697f6093SHanjun Guo u32 id = id_in; 34988ef16d8STomasz Nowicki 35088ef16d8STomasz Nowicki /* Parse the ID mapping tree to find specified node type */ 35188ef16d8STomasz Nowicki while (node) { 35288ef16d8STomasz Nowicki struct acpi_iort_id_mapping *map; 35388ef16d8STomasz Nowicki int i; 35488ef16d8STomasz Nowicki 355ea50b524SLorenzo Pieralisi if (IORT_TYPE_MASK(node->type) & type_mask) { 356697f6093SHanjun Guo if (id_out) 357697f6093SHanjun Guo *id_out = id; 35888ef16d8STomasz Nowicki return node; 35988ef16d8STomasz Nowicki } 36088ef16d8STomasz Nowicki 36188ef16d8STomasz Nowicki if (!node->mapping_offset || !node->mapping_count) 36288ef16d8STomasz Nowicki goto fail_map; 36388ef16d8STomasz Nowicki 36488ef16d8STomasz Nowicki map = ACPI_ADD_PTR(struct acpi_iort_id_mapping, node, 36588ef16d8STomasz Nowicki node->mapping_offset); 36688ef16d8STomasz Nowicki 36788ef16d8STomasz Nowicki /* Firmware bug! */ 36888ef16d8STomasz Nowicki if (!map->output_reference) { 36988ef16d8STomasz Nowicki pr_err(FW_BUG "[node %p type %d] ID map has NULL parent reference\n", 37088ef16d8STomasz Nowicki node, node->type); 37188ef16d8STomasz Nowicki goto fail_map; 37288ef16d8STomasz Nowicki } 37388ef16d8STomasz Nowicki 374697f6093SHanjun Guo /* Do the ID translation */ 37588ef16d8STomasz Nowicki for (i = 0; i < node->mapping_count; i++, map++) { 376697f6093SHanjun Guo if (!iort_id_map(map, node->type, id, &id)) 37788ef16d8STomasz Nowicki break; 37888ef16d8STomasz Nowicki } 37988ef16d8STomasz Nowicki 38088ef16d8STomasz Nowicki if (i == node->mapping_count) 38188ef16d8STomasz Nowicki goto fail_map; 38288ef16d8STomasz Nowicki 38388ef16d8STomasz Nowicki node = ACPI_ADD_PTR(struct acpi_iort_node, iort_table, 38488ef16d8STomasz Nowicki map->output_reference); 38588ef16d8STomasz Nowicki } 38688ef16d8STomasz Nowicki 38788ef16d8STomasz Nowicki fail_map: 388697f6093SHanjun Guo /* Map input ID to output ID unchanged on mapping failure */ 389697f6093SHanjun Guo if (id_out) 390697f6093SHanjun Guo *id_out = id_in; 39188ef16d8STomasz Nowicki 39288ef16d8STomasz Nowicki return NULL; 39388ef16d8STomasz Nowicki } 39488ef16d8STomasz Nowicki 3958ca4f1d3SHanjun Guo static 3968ca4f1d3SHanjun Guo struct acpi_iort_node *iort_node_map_platform_id(struct acpi_iort_node *node, 3978ca4f1d3SHanjun Guo u32 *id_out, u8 type_mask, 3988ca4f1d3SHanjun Guo int index) 3998ca4f1d3SHanjun Guo { 4008ca4f1d3SHanjun Guo struct acpi_iort_node *parent; 4018ca4f1d3SHanjun Guo u32 id; 4028ca4f1d3SHanjun Guo 4038ca4f1d3SHanjun Guo /* step 1: retrieve the initial dev id */ 4048ca4f1d3SHanjun Guo parent = iort_node_get_id(node, &id, index); 4058ca4f1d3SHanjun Guo if (!parent) 4068ca4f1d3SHanjun Guo return NULL; 4078ca4f1d3SHanjun Guo 4088ca4f1d3SHanjun Guo /* 4098ca4f1d3SHanjun Guo * optional step 2: map the initial dev id if its parent is not 4108ca4f1d3SHanjun Guo * the target type we want, map it again for the use cases such 4118ca4f1d3SHanjun Guo * as NC (named component) -> SMMU -> ITS. If the type is matched, 4128ca4f1d3SHanjun Guo * return the initial dev id and its parent pointer directly. 4138ca4f1d3SHanjun Guo */ 4148ca4f1d3SHanjun Guo if (!(IORT_TYPE_MASK(parent->type) & type_mask)) 4158ca4f1d3SHanjun Guo parent = iort_node_map_id(parent, id, id_out, type_mask); 4168ca4f1d3SHanjun Guo else 4178ca4f1d3SHanjun Guo if (id_out) 4188ca4f1d3SHanjun Guo *id_out = id; 4198ca4f1d3SHanjun Guo 4208ca4f1d3SHanjun Guo return parent; 4218ca4f1d3SHanjun Guo } 4228ca4f1d3SHanjun Guo 42388ef16d8STomasz Nowicki static struct acpi_iort_node *iort_find_dev_node(struct device *dev) 42488ef16d8STomasz Nowicki { 42588ef16d8STomasz Nowicki struct pci_bus *pbus; 42688ef16d8STomasz Nowicki 42788ef16d8STomasz Nowicki if (!dev_is_pci(dev)) 42888ef16d8STomasz Nowicki return iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT, 42988ef16d8STomasz Nowicki iort_match_node_callback, dev); 43088ef16d8STomasz Nowicki 43188ef16d8STomasz Nowicki /* Find a PCI root bus */ 43288ef16d8STomasz Nowicki pbus = to_pci_dev(dev)->bus; 43388ef16d8STomasz Nowicki while (!pci_is_root_bus(pbus)) 43488ef16d8STomasz Nowicki pbus = pbus->parent; 43588ef16d8STomasz Nowicki 43688ef16d8STomasz Nowicki return iort_scan_node(ACPI_IORT_NODE_PCI_ROOT_COMPLEX, 43788ef16d8STomasz Nowicki iort_match_node_callback, &pbus->dev); 43888ef16d8STomasz Nowicki } 43988ef16d8STomasz Nowicki 4404bf2efd2STomasz Nowicki /** 4414bf2efd2STomasz Nowicki * iort_msi_map_rid() - Map a MSI requester ID for a device 4424bf2efd2STomasz Nowicki * @dev: The device for which the mapping is to be done. 4434bf2efd2STomasz Nowicki * @req_id: The device requester ID. 4444bf2efd2STomasz Nowicki * 4454bf2efd2STomasz Nowicki * Returns: mapped MSI RID on success, input requester ID otherwise 4464bf2efd2STomasz Nowicki */ 4474bf2efd2STomasz Nowicki u32 iort_msi_map_rid(struct device *dev, u32 req_id) 4484bf2efd2STomasz Nowicki { 4494bf2efd2STomasz Nowicki struct acpi_iort_node *node; 4504bf2efd2STomasz Nowicki u32 dev_id; 4514bf2efd2STomasz Nowicki 4524bf2efd2STomasz Nowicki node = iort_find_dev_node(dev); 4534bf2efd2STomasz Nowicki if (!node) 4544bf2efd2STomasz Nowicki return req_id; 4554bf2efd2STomasz Nowicki 456697f6093SHanjun Guo iort_node_map_id(node, req_id, &dev_id, IORT_MSI_TYPE); 4574bf2efd2STomasz Nowicki return dev_id; 4584bf2efd2STomasz Nowicki } 4594bf2efd2STomasz Nowicki 4604bf2efd2STomasz Nowicki /** 461ae7c1838SHanjun Guo * iort_pmsi_get_dev_id() - Get the device id for a device 462ae7c1838SHanjun Guo * @dev: The device for which the mapping is to be done. 463ae7c1838SHanjun Guo * @dev_id: The device ID found. 464ae7c1838SHanjun Guo * 465ae7c1838SHanjun Guo * Returns: 0 for successful find a dev id, -ENODEV on error 466ae7c1838SHanjun Guo */ 467ae7c1838SHanjun Guo int iort_pmsi_get_dev_id(struct device *dev, u32 *dev_id) 468ae7c1838SHanjun Guo { 469ae7c1838SHanjun Guo int i; 470ae7c1838SHanjun Guo struct acpi_iort_node *node; 471ae7c1838SHanjun Guo 472ae7c1838SHanjun Guo node = iort_find_dev_node(dev); 473ae7c1838SHanjun Guo if (!node) 474ae7c1838SHanjun Guo return -ENODEV; 475ae7c1838SHanjun Guo 476ae7c1838SHanjun Guo for (i = 0; i < node->mapping_count; i++) { 477ae7c1838SHanjun Guo if (iort_node_map_platform_id(node, dev_id, IORT_MSI_TYPE, i)) 478ae7c1838SHanjun Guo return 0; 479ae7c1838SHanjun Guo } 480ae7c1838SHanjun Guo 481ae7c1838SHanjun Guo return -ENODEV; 482ae7c1838SHanjun Guo } 483ae7c1838SHanjun Guo 484ae7c1838SHanjun Guo /** 4854bf2efd2STomasz Nowicki * iort_dev_find_its_id() - Find the ITS identifier for a device 4864bf2efd2STomasz Nowicki * @dev: The device. 4876cb6bf56SHanjun Guo * @req_id: Device's requester ID 4884bf2efd2STomasz Nowicki * @idx: Index of the ITS identifier list. 4894bf2efd2STomasz Nowicki * @its_id: ITS identifier. 4904bf2efd2STomasz Nowicki * 4914bf2efd2STomasz Nowicki * Returns: 0 on success, appropriate error value otherwise 4924bf2efd2STomasz Nowicki */ 4934bf2efd2STomasz Nowicki static int iort_dev_find_its_id(struct device *dev, u32 req_id, 4944bf2efd2STomasz Nowicki unsigned int idx, int *its_id) 4954bf2efd2STomasz Nowicki { 4964bf2efd2STomasz Nowicki struct acpi_iort_its_group *its; 4974bf2efd2STomasz Nowicki struct acpi_iort_node *node; 4984bf2efd2STomasz Nowicki 4994bf2efd2STomasz Nowicki node = iort_find_dev_node(dev); 5004bf2efd2STomasz Nowicki if (!node) 5014bf2efd2STomasz Nowicki return -ENXIO; 5024bf2efd2STomasz Nowicki 503697f6093SHanjun Guo node = iort_node_map_id(node, req_id, NULL, IORT_MSI_TYPE); 5044bf2efd2STomasz Nowicki if (!node) 5054bf2efd2STomasz Nowicki return -ENXIO; 5064bf2efd2STomasz Nowicki 5074bf2efd2STomasz Nowicki /* Move to ITS specific data */ 5084bf2efd2STomasz Nowicki its = (struct acpi_iort_its_group *)node->node_data; 5094bf2efd2STomasz Nowicki if (idx > its->its_count) { 5104bf2efd2STomasz Nowicki dev_err(dev, "requested ITS ID index [%d] is greater than available [%d]\n", 5114bf2efd2STomasz Nowicki idx, its->its_count); 5124bf2efd2STomasz Nowicki return -ENXIO; 5134bf2efd2STomasz Nowicki } 5144bf2efd2STomasz Nowicki 5154bf2efd2STomasz Nowicki *its_id = its->identifiers[idx]; 5164bf2efd2STomasz Nowicki return 0; 5174bf2efd2STomasz Nowicki } 5184bf2efd2STomasz Nowicki 5194bf2efd2STomasz Nowicki /** 5204bf2efd2STomasz Nowicki * iort_get_device_domain() - Find MSI domain related to a device 5214bf2efd2STomasz Nowicki * @dev: The device. 5224bf2efd2STomasz Nowicki * @req_id: Requester ID for the device. 5234bf2efd2STomasz Nowicki * 5244bf2efd2STomasz Nowicki * Returns: the MSI domain for this device, NULL otherwise 5254bf2efd2STomasz Nowicki */ 5264bf2efd2STomasz Nowicki struct irq_domain *iort_get_device_domain(struct device *dev, u32 req_id) 5274bf2efd2STomasz Nowicki { 5284bf2efd2STomasz Nowicki struct fwnode_handle *handle; 5294bf2efd2STomasz Nowicki int its_id; 5304bf2efd2STomasz Nowicki 5314bf2efd2STomasz Nowicki if (iort_dev_find_its_id(dev, req_id, 0, &its_id)) 5324bf2efd2STomasz Nowicki return NULL; 5334bf2efd2STomasz Nowicki 5344bf2efd2STomasz Nowicki handle = iort_find_domain_token(its_id); 5354bf2efd2STomasz Nowicki if (!handle) 5364bf2efd2STomasz Nowicki return NULL; 5374bf2efd2STomasz Nowicki 5384bf2efd2STomasz Nowicki return irq_find_matching_fwnode(handle, DOMAIN_BUS_PCI_MSI); 5394bf2efd2STomasz Nowicki } 5404bf2efd2STomasz Nowicki 541d4f54a18SHanjun Guo /** 542d4f54a18SHanjun Guo * iort_get_platform_device_domain() - Find MSI domain related to a 543d4f54a18SHanjun Guo * platform device 544d4f54a18SHanjun Guo * @dev: the dev pointer associated with the platform device 545d4f54a18SHanjun Guo * 546d4f54a18SHanjun Guo * Returns: the MSI domain for this device, NULL otherwise 547d4f54a18SHanjun Guo */ 548d4f54a18SHanjun Guo static struct irq_domain *iort_get_platform_device_domain(struct device *dev) 549d4f54a18SHanjun Guo { 550d4f54a18SHanjun Guo struct acpi_iort_node *node, *msi_parent; 551d4f54a18SHanjun Guo struct fwnode_handle *iort_fwnode; 552d4f54a18SHanjun Guo struct acpi_iort_its_group *its; 553d4f54a18SHanjun Guo int i; 554d4f54a18SHanjun Guo 555d4f54a18SHanjun Guo /* find its associated iort node */ 556d4f54a18SHanjun Guo node = iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT, 557d4f54a18SHanjun Guo iort_match_node_callback, dev); 558d4f54a18SHanjun Guo if (!node) 559d4f54a18SHanjun Guo return NULL; 560d4f54a18SHanjun Guo 561d4f54a18SHanjun Guo /* then find its msi parent node */ 562d4f54a18SHanjun Guo for (i = 0; i < node->mapping_count; i++) { 563d4f54a18SHanjun Guo msi_parent = iort_node_map_platform_id(node, NULL, 564d4f54a18SHanjun Guo IORT_MSI_TYPE, i); 565d4f54a18SHanjun Guo if (msi_parent) 566d4f54a18SHanjun Guo break; 567d4f54a18SHanjun Guo } 568d4f54a18SHanjun Guo 569d4f54a18SHanjun Guo if (!msi_parent) 570d4f54a18SHanjun Guo return NULL; 571d4f54a18SHanjun Guo 572d4f54a18SHanjun Guo /* Move to ITS specific data */ 573d4f54a18SHanjun Guo its = (struct acpi_iort_its_group *)msi_parent->node_data; 574d4f54a18SHanjun Guo 575d4f54a18SHanjun Guo iort_fwnode = iort_find_domain_token(its->identifiers[0]); 576d4f54a18SHanjun Guo if (!iort_fwnode) 577d4f54a18SHanjun Guo return NULL; 578d4f54a18SHanjun Guo 579d4f54a18SHanjun Guo return irq_find_matching_fwnode(iort_fwnode, DOMAIN_BUS_PLATFORM_MSI); 580d4f54a18SHanjun Guo } 581d4f54a18SHanjun Guo 582d4f54a18SHanjun Guo void acpi_configure_pmsi_domain(struct device *dev) 583d4f54a18SHanjun Guo { 584d4f54a18SHanjun Guo struct irq_domain *msi_domain; 585d4f54a18SHanjun Guo 586d4f54a18SHanjun Guo msi_domain = iort_get_platform_device_domain(dev); 587d4f54a18SHanjun Guo if (msi_domain) 588d4f54a18SHanjun Guo dev_set_msi_domain(dev, msi_domain); 589d4f54a18SHanjun Guo } 590d4f54a18SHanjun Guo 591bc8648d4SRobin Murphy static int __maybe_unused __get_pci_rid(struct pci_dev *pdev, u16 alias, 592bc8648d4SRobin Murphy void *data) 593643b8e4dSLorenzo Pieralisi { 594643b8e4dSLorenzo Pieralisi u32 *rid = data; 595643b8e4dSLorenzo Pieralisi 596643b8e4dSLorenzo Pieralisi *rid = alias; 597643b8e4dSLorenzo Pieralisi return 0; 598643b8e4dSLorenzo Pieralisi } 599643b8e4dSLorenzo Pieralisi 600643b8e4dSLorenzo Pieralisi static int arm_smmu_iort_xlate(struct device *dev, u32 streamid, 601643b8e4dSLorenzo Pieralisi struct fwnode_handle *fwnode, 602643b8e4dSLorenzo Pieralisi const struct iommu_ops *ops) 603643b8e4dSLorenzo Pieralisi { 604643b8e4dSLorenzo Pieralisi int ret = iommu_fwspec_init(dev, fwnode, ops); 605643b8e4dSLorenzo Pieralisi 606643b8e4dSLorenzo Pieralisi if (!ret) 607643b8e4dSLorenzo Pieralisi ret = iommu_fwspec_add_ids(dev, &streamid, 1); 608643b8e4dSLorenzo Pieralisi 609643b8e4dSLorenzo Pieralisi return ret; 610643b8e4dSLorenzo Pieralisi } 611643b8e4dSLorenzo Pieralisi 6121d9029d4SLorenzo Pieralisi static inline bool iort_iommu_driver_enabled(u8 type) 6131d9029d4SLorenzo Pieralisi { 6141d9029d4SLorenzo Pieralisi switch (type) { 6151d9029d4SLorenzo Pieralisi case ACPI_IORT_NODE_SMMU_V3: 6161d9029d4SLorenzo Pieralisi return IS_BUILTIN(CONFIG_ARM_SMMU_V3); 6171d9029d4SLorenzo Pieralisi case ACPI_IORT_NODE_SMMU: 6181d9029d4SLorenzo Pieralisi return IS_BUILTIN(CONFIG_ARM_SMMU); 6191d9029d4SLorenzo Pieralisi default: 6201d9029d4SLorenzo Pieralisi pr_warn("IORT node type %u does not describe an SMMU\n", type); 6211d9029d4SLorenzo Pieralisi return false; 6221d9029d4SLorenzo Pieralisi } 6231d9029d4SLorenzo Pieralisi } 6241d9029d4SLorenzo Pieralisi 625d49f2dedSLorenzo Pieralisi #ifdef CONFIG_IOMMU_API 626d49f2dedSLorenzo Pieralisi static inline 627d49f2dedSLorenzo Pieralisi const struct iommu_ops *iort_fwspec_iommu_ops(struct iommu_fwspec *fwspec) 628d49f2dedSLorenzo Pieralisi { 629d49f2dedSLorenzo Pieralisi return (fwspec && fwspec->ops) ? fwspec->ops : NULL; 630d49f2dedSLorenzo Pieralisi } 631d49f2dedSLorenzo Pieralisi 632d49f2dedSLorenzo Pieralisi static inline 633d49f2dedSLorenzo Pieralisi int iort_add_device_replay(const struct iommu_ops *ops, struct device *dev) 634d49f2dedSLorenzo Pieralisi { 635d49f2dedSLorenzo Pieralisi int err = 0; 636d49f2dedSLorenzo Pieralisi 637bc8648d4SRobin Murphy if (ops->add_device && dev->bus && !dev->iommu_group) 638d49f2dedSLorenzo Pieralisi err = ops->add_device(dev); 639d49f2dedSLorenzo Pieralisi 640d49f2dedSLorenzo Pieralisi return err; 641d49f2dedSLorenzo Pieralisi } 642d49f2dedSLorenzo Pieralisi #else 643d49f2dedSLorenzo Pieralisi static inline 644d49f2dedSLorenzo Pieralisi const struct iommu_ops *iort_fwspec_iommu_ops(struct iommu_fwspec *fwspec) 645d49f2dedSLorenzo Pieralisi { return NULL; } 646d49f2dedSLorenzo Pieralisi static inline 647d49f2dedSLorenzo Pieralisi int iort_add_device_replay(const struct iommu_ops *ops, struct device *dev) 648d49f2dedSLorenzo Pieralisi { return 0; } 649d49f2dedSLorenzo Pieralisi #endif 650d49f2dedSLorenzo Pieralisi 651bc8648d4SRobin Murphy static int iort_iommu_xlate(struct device *dev, struct acpi_iort_node *node, 652643b8e4dSLorenzo Pieralisi u32 streamid) 653643b8e4dSLorenzo Pieralisi { 654bc8648d4SRobin Murphy const struct iommu_ops *ops; 655643b8e4dSLorenzo Pieralisi struct fwnode_handle *iort_fwnode; 656643b8e4dSLorenzo Pieralisi 657bc8648d4SRobin Murphy if (!node) 658bc8648d4SRobin Murphy return -ENODEV; 659bc8648d4SRobin Murphy 660643b8e4dSLorenzo Pieralisi iort_fwnode = iort_get_fwnode(node); 661643b8e4dSLorenzo Pieralisi if (!iort_fwnode) 662bc8648d4SRobin Murphy return -ENODEV; 663643b8e4dSLorenzo Pieralisi 6645a1bb638SSricharan R /* 6655a1bb638SSricharan R * If the ops look-up fails, this means that either 6665a1bb638SSricharan R * the SMMU drivers have not been probed yet or that 6675a1bb638SSricharan R * the SMMU drivers are not built in the kernel; 6685a1bb638SSricharan R * Depending on whether the SMMU drivers are built-in 6695a1bb638SSricharan R * in the kernel or not, defer the IOMMU configuration 6705a1bb638SSricharan R * or just abort it. 6715a1bb638SSricharan R */ 672bc8648d4SRobin Murphy ops = iommu_ops_from_fwnode(iort_fwnode); 673643b8e4dSLorenzo Pieralisi if (!ops) 6745a1bb638SSricharan R return iort_iommu_driver_enabled(node->type) ? 675bc8648d4SRobin Murphy -EPROBE_DEFER : -ENODEV; 676643b8e4dSLorenzo Pieralisi 677bc8648d4SRobin Murphy return arm_smmu_iort_xlate(dev, streamid, iort_fwnode, ops); 678643b8e4dSLorenzo Pieralisi } 679643b8e4dSLorenzo Pieralisi 680bc8648d4SRobin Murphy struct iort_pci_alias_info { 681bc8648d4SRobin Murphy struct device *dev; 682bc8648d4SRobin Murphy struct acpi_iort_node *node; 683bc8648d4SRobin Murphy }; 684bc8648d4SRobin Murphy 685bc8648d4SRobin Murphy static int iort_pci_iommu_init(struct pci_dev *pdev, u16 alias, void *data) 686bc8648d4SRobin Murphy { 687bc8648d4SRobin Murphy struct iort_pci_alias_info *info = data; 688bc8648d4SRobin Murphy struct acpi_iort_node *parent; 689bc8648d4SRobin Murphy u32 streamid; 690bc8648d4SRobin Murphy 691bc8648d4SRobin Murphy parent = iort_node_map_id(info->node, alias, &streamid, 692bc8648d4SRobin Murphy IORT_IOMMU_TYPE); 693bc8648d4SRobin Murphy return iort_iommu_xlate(info->dev, parent, streamid); 694643b8e4dSLorenzo Pieralisi } 695643b8e4dSLorenzo Pieralisi 696643b8e4dSLorenzo Pieralisi /** 69718b709beSLorenzo Pieralisi * iort_set_dma_mask - Set-up dma mask for a device. 69818b709beSLorenzo Pieralisi * 69918b709beSLorenzo Pieralisi * @dev: device to configure 70018b709beSLorenzo Pieralisi */ 70118b709beSLorenzo Pieralisi void iort_set_dma_mask(struct device *dev) 70218b709beSLorenzo Pieralisi { 70318b709beSLorenzo Pieralisi /* 70418b709beSLorenzo Pieralisi * Set default coherent_dma_mask to 32 bit. Drivers are expected to 70518b709beSLorenzo Pieralisi * setup the correct supported mask. 70618b709beSLorenzo Pieralisi */ 70718b709beSLorenzo Pieralisi if (!dev->coherent_dma_mask) 70818b709beSLorenzo Pieralisi dev->coherent_dma_mask = DMA_BIT_MASK(32); 70918b709beSLorenzo Pieralisi 71018b709beSLorenzo Pieralisi /* 71118b709beSLorenzo Pieralisi * Set it to coherent_dma_mask by default if the architecture 71218b709beSLorenzo Pieralisi * code has not set it. 71318b709beSLorenzo Pieralisi */ 71418b709beSLorenzo Pieralisi if (!dev->dma_mask) 71518b709beSLorenzo Pieralisi dev->dma_mask = &dev->coherent_dma_mask; 71618b709beSLorenzo Pieralisi } 71718b709beSLorenzo Pieralisi 71818b709beSLorenzo Pieralisi /** 719643b8e4dSLorenzo Pieralisi * iort_iommu_configure - Set-up IOMMU configuration for a device. 720643b8e4dSLorenzo Pieralisi * 721643b8e4dSLorenzo Pieralisi * @dev: device to configure 722643b8e4dSLorenzo Pieralisi * 723643b8e4dSLorenzo Pieralisi * Returns: iommu_ops pointer on configuration success 724643b8e4dSLorenzo Pieralisi * NULL on configuration failure 725643b8e4dSLorenzo Pieralisi */ 726643b8e4dSLorenzo Pieralisi const struct iommu_ops *iort_iommu_configure(struct device *dev) 727643b8e4dSLorenzo Pieralisi { 728643b8e4dSLorenzo Pieralisi struct acpi_iort_node *node, *parent; 729bc8648d4SRobin Murphy const struct iommu_ops *ops; 730643b8e4dSLorenzo Pieralisi u32 streamid = 0; 731bc8648d4SRobin Murphy int err = -ENODEV; 732643b8e4dSLorenzo Pieralisi 7334dac3210SLorenzo Pieralisi /* 7344dac3210SLorenzo Pieralisi * If we already translated the fwspec there 7354dac3210SLorenzo Pieralisi * is nothing left to do, return the iommu_ops. 7364dac3210SLorenzo Pieralisi */ 7374dac3210SLorenzo Pieralisi ops = iort_fwspec_iommu_ops(dev->iommu_fwspec); 7384dac3210SLorenzo Pieralisi if (ops) 7394dac3210SLorenzo Pieralisi return ops; 7404dac3210SLorenzo Pieralisi 741643b8e4dSLorenzo Pieralisi if (dev_is_pci(dev)) { 742643b8e4dSLorenzo Pieralisi struct pci_bus *bus = to_pci_dev(dev)->bus; 743bc8648d4SRobin Murphy struct iort_pci_alias_info info = { .dev = dev }; 744643b8e4dSLorenzo Pieralisi 745643b8e4dSLorenzo Pieralisi node = iort_scan_node(ACPI_IORT_NODE_PCI_ROOT_COMPLEX, 746643b8e4dSLorenzo Pieralisi iort_match_node_callback, &bus->dev); 747643b8e4dSLorenzo Pieralisi if (!node) 748643b8e4dSLorenzo Pieralisi return NULL; 749643b8e4dSLorenzo Pieralisi 750bc8648d4SRobin Murphy info.node = node; 751bc8648d4SRobin Murphy err = pci_for_each_dma_alias(to_pci_dev(dev), 752bc8648d4SRobin Murphy iort_pci_iommu_init, &info); 753643b8e4dSLorenzo Pieralisi } else { 754643b8e4dSLorenzo Pieralisi int i = 0; 755643b8e4dSLorenzo Pieralisi 756643b8e4dSLorenzo Pieralisi node = iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT, 757643b8e4dSLorenzo Pieralisi iort_match_node_callback, dev); 758643b8e4dSLorenzo Pieralisi if (!node) 759643b8e4dSLorenzo Pieralisi return NULL; 760643b8e4dSLorenzo Pieralisi 761bc8648d4SRobin Murphy do { 7628ca4f1d3SHanjun Guo parent = iort_node_map_platform_id(node, &streamid, 7638ca4f1d3SHanjun Guo IORT_IOMMU_TYPE, 7648ca4f1d3SHanjun Guo i++); 765bc8648d4SRobin Murphy 766bc8648d4SRobin Murphy if (parent) 767bc8648d4SRobin Murphy err = iort_iommu_xlate(dev, parent, streamid); 768bc8648d4SRobin Murphy } while (parent && !err); 769643b8e4dSLorenzo Pieralisi } 770643b8e4dSLorenzo Pieralisi 7715a1bb638SSricharan R /* 7725a1bb638SSricharan R * If we have reason to believe the IOMMU driver missed the initial 7735a1bb638SSricharan R * add_device callback for dev, replay it to get things in order. 7745a1bb638SSricharan R */ 775bc8648d4SRobin Murphy if (!err) { 776bc8648d4SRobin Murphy ops = dev->iommu_fwspec->ops; 777d49f2dedSLorenzo Pieralisi err = iort_add_device_replay(ops, dev); 778bc8648d4SRobin Murphy } 7795a1bb638SSricharan R 780058f8c3fSSricharan R /* Ignore all other errors apart from EPROBE_DEFER */ 781bc8648d4SRobin Murphy if (err == -EPROBE_DEFER) { 782bc8648d4SRobin Murphy ops = ERR_PTR(err); 783bc8648d4SRobin Murphy } else if (err) { 784bc8648d4SRobin Murphy dev_dbg(dev, "Adding to IOMMU failed: %d\n", err); 785058f8c3fSSricharan R ops = NULL; 786058f8c3fSSricharan R } 787058f8c3fSSricharan R 788643b8e4dSLorenzo Pieralisi return ops; 789643b8e4dSLorenzo Pieralisi } 790643b8e4dSLorenzo Pieralisi 791e4dadfa8SLorenzo Pieralisi static void __init acpi_iort_register_irq(int hwirq, const char *name, 792e4dadfa8SLorenzo Pieralisi int trigger, 793e4dadfa8SLorenzo Pieralisi struct resource *res) 794e4dadfa8SLorenzo Pieralisi { 795e4dadfa8SLorenzo Pieralisi int irq = acpi_register_gsi(NULL, hwirq, trigger, 796e4dadfa8SLorenzo Pieralisi ACPI_ACTIVE_HIGH); 797e4dadfa8SLorenzo Pieralisi 798e4dadfa8SLorenzo Pieralisi if (irq <= 0) { 799e4dadfa8SLorenzo Pieralisi pr_err("could not register gsi hwirq %d name [%s]\n", hwirq, 800e4dadfa8SLorenzo Pieralisi name); 801e4dadfa8SLorenzo Pieralisi return; 802e4dadfa8SLorenzo Pieralisi } 803e4dadfa8SLorenzo Pieralisi 804e4dadfa8SLorenzo Pieralisi res->start = irq; 805e4dadfa8SLorenzo Pieralisi res->end = irq; 806e4dadfa8SLorenzo Pieralisi res->flags = IORESOURCE_IRQ; 807e4dadfa8SLorenzo Pieralisi res->name = name; 808e4dadfa8SLorenzo Pieralisi } 809e4dadfa8SLorenzo Pieralisi 810e4dadfa8SLorenzo Pieralisi static int __init arm_smmu_v3_count_resources(struct acpi_iort_node *node) 811e4dadfa8SLorenzo Pieralisi { 812e4dadfa8SLorenzo Pieralisi struct acpi_iort_smmu_v3 *smmu; 813e4dadfa8SLorenzo Pieralisi /* Always present mem resource */ 814e4dadfa8SLorenzo Pieralisi int num_res = 1; 815e4dadfa8SLorenzo Pieralisi 816e4dadfa8SLorenzo Pieralisi /* Retrieve SMMUv3 specific data */ 817e4dadfa8SLorenzo Pieralisi smmu = (struct acpi_iort_smmu_v3 *)node->node_data; 818e4dadfa8SLorenzo Pieralisi 819e4dadfa8SLorenzo Pieralisi if (smmu->event_gsiv) 820e4dadfa8SLorenzo Pieralisi num_res++; 821e4dadfa8SLorenzo Pieralisi 822e4dadfa8SLorenzo Pieralisi if (smmu->pri_gsiv) 823e4dadfa8SLorenzo Pieralisi num_res++; 824e4dadfa8SLorenzo Pieralisi 825e4dadfa8SLorenzo Pieralisi if (smmu->gerr_gsiv) 826e4dadfa8SLorenzo Pieralisi num_res++; 827e4dadfa8SLorenzo Pieralisi 828e4dadfa8SLorenzo Pieralisi if (smmu->sync_gsiv) 829e4dadfa8SLorenzo Pieralisi num_res++; 830e4dadfa8SLorenzo Pieralisi 831e4dadfa8SLorenzo Pieralisi return num_res; 832e4dadfa8SLorenzo Pieralisi } 833e4dadfa8SLorenzo Pieralisi 834f935448aSGeetha Sowjanya static bool arm_smmu_v3_is_combined_irq(struct acpi_iort_smmu_v3 *smmu) 835f935448aSGeetha Sowjanya { 836f935448aSGeetha Sowjanya /* 837f935448aSGeetha Sowjanya * Cavium ThunderX2 implementation doesn't not support unique 838f935448aSGeetha Sowjanya * irq line. Use single irq line for all the SMMUv3 interrupts. 839f935448aSGeetha Sowjanya */ 840f935448aSGeetha Sowjanya if (smmu->model != ACPI_IORT_SMMU_V3_CAVIUM_CN99XX) 841f935448aSGeetha Sowjanya return false; 842f935448aSGeetha Sowjanya 843f935448aSGeetha Sowjanya /* 844f935448aSGeetha Sowjanya * ThunderX2 doesn't support MSIs from the SMMU, so we're checking 845f935448aSGeetha Sowjanya * SPI numbers here. 846f935448aSGeetha Sowjanya */ 847f935448aSGeetha Sowjanya return smmu->event_gsiv == smmu->pri_gsiv && 848f935448aSGeetha Sowjanya smmu->event_gsiv == smmu->gerr_gsiv && 849f935448aSGeetha Sowjanya smmu->event_gsiv == smmu->sync_gsiv; 850f935448aSGeetha Sowjanya } 851f935448aSGeetha Sowjanya 852403e8c7cSLinu Cherian static unsigned long arm_smmu_v3_resource_size(struct acpi_iort_smmu_v3 *smmu) 853403e8c7cSLinu Cherian { 854403e8c7cSLinu Cherian /* 855403e8c7cSLinu Cherian * Override the size, for Cavium ThunderX2 implementation 856403e8c7cSLinu Cherian * which doesn't support the page 1 SMMU register space. 857403e8c7cSLinu Cherian */ 858403e8c7cSLinu Cherian if (smmu->model == ACPI_IORT_SMMU_V3_CAVIUM_CN99XX) 859403e8c7cSLinu Cherian return SZ_64K; 860403e8c7cSLinu Cherian 861403e8c7cSLinu Cherian return SZ_128K; 862403e8c7cSLinu Cherian } 863403e8c7cSLinu Cherian 864e4dadfa8SLorenzo Pieralisi static void __init arm_smmu_v3_init_resources(struct resource *res, 865e4dadfa8SLorenzo Pieralisi struct acpi_iort_node *node) 866e4dadfa8SLorenzo Pieralisi { 867e4dadfa8SLorenzo Pieralisi struct acpi_iort_smmu_v3 *smmu; 868e4dadfa8SLorenzo Pieralisi int num_res = 0; 869e4dadfa8SLorenzo Pieralisi 870e4dadfa8SLorenzo Pieralisi /* Retrieve SMMUv3 specific data */ 871e4dadfa8SLorenzo Pieralisi smmu = (struct acpi_iort_smmu_v3 *)node->node_data; 872e4dadfa8SLorenzo Pieralisi 873e4dadfa8SLorenzo Pieralisi res[num_res].start = smmu->base_address; 874403e8c7cSLinu Cherian res[num_res].end = smmu->base_address + 875403e8c7cSLinu Cherian arm_smmu_v3_resource_size(smmu) - 1; 876e4dadfa8SLorenzo Pieralisi res[num_res].flags = IORESOURCE_MEM; 877e4dadfa8SLorenzo Pieralisi 878e4dadfa8SLorenzo Pieralisi num_res++; 879f935448aSGeetha Sowjanya if (arm_smmu_v3_is_combined_irq(smmu)) { 880f935448aSGeetha Sowjanya if (smmu->event_gsiv) 881f935448aSGeetha Sowjanya acpi_iort_register_irq(smmu->event_gsiv, "combined", 882f935448aSGeetha Sowjanya ACPI_EDGE_SENSITIVE, 883f935448aSGeetha Sowjanya &res[num_res++]); 884f935448aSGeetha Sowjanya } else { 885e4dadfa8SLorenzo Pieralisi 886e4dadfa8SLorenzo Pieralisi if (smmu->event_gsiv) 887e4dadfa8SLorenzo Pieralisi acpi_iort_register_irq(smmu->event_gsiv, "eventq", 888e4dadfa8SLorenzo Pieralisi ACPI_EDGE_SENSITIVE, 889e4dadfa8SLorenzo Pieralisi &res[num_res++]); 890e4dadfa8SLorenzo Pieralisi 891e4dadfa8SLorenzo Pieralisi if (smmu->pri_gsiv) 892e4dadfa8SLorenzo Pieralisi acpi_iort_register_irq(smmu->pri_gsiv, "priq", 893e4dadfa8SLorenzo Pieralisi ACPI_EDGE_SENSITIVE, 894e4dadfa8SLorenzo Pieralisi &res[num_res++]); 895e4dadfa8SLorenzo Pieralisi 896e4dadfa8SLorenzo Pieralisi if (smmu->gerr_gsiv) 897e4dadfa8SLorenzo Pieralisi acpi_iort_register_irq(smmu->gerr_gsiv, "gerror", 898e4dadfa8SLorenzo Pieralisi ACPI_EDGE_SENSITIVE, 899e4dadfa8SLorenzo Pieralisi &res[num_res++]); 900e4dadfa8SLorenzo Pieralisi 901e4dadfa8SLorenzo Pieralisi if (smmu->sync_gsiv) 902e4dadfa8SLorenzo Pieralisi acpi_iort_register_irq(smmu->sync_gsiv, "cmdq-sync", 903e4dadfa8SLorenzo Pieralisi ACPI_EDGE_SENSITIVE, 904e4dadfa8SLorenzo Pieralisi &res[num_res++]); 905e4dadfa8SLorenzo Pieralisi } 906f935448aSGeetha Sowjanya } 907e4dadfa8SLorenzo Pieralisi 908e4dadfa8SLorenzo Pieralisi static bool __init arm_smmu_v3_is_coherent(struct acpi_iort_node *node) 909e4dadfa8SLorenzo Pieralisi { 910e4dadfa8SLorenzo Pieralisi struct acpi_iort_smmu_v3 *smmu; 911e4dadfa8SLorenzo Pieralisi 912e4dadfa8SLorenzo Pieralisi /* Retrieve SMMUv3 specific data */ 913e4dadfa8SLorenzo Pieralisi smmu = (struct acpi_iort_smmu_v3 *)node->node_data; 914e4dadfa8SLorenzo Pieralisi 915e4dadfa8SLorenzo Pieralisi return smmu->flags & ACPI_IORT_SMMU_V3_COHACC_OVERRIDE; 916e4dadfa8SLorenzo Pieralisi } 917e4dadfa8SLorenzo Pieralisi 918*5fe0ce3bSGanapatrao Kulkarni #if defined(CONFIG_ACPI_NUMA) && defined(ACPI_IORT_SMMU_V3_PXM_VALID) 919*5fe0ce3bSGanapatrao Kulkarni /* 920*5fe0ce3bSGanapatrao Kulkarni * set numa proximity domain for smmuv3 device 921*5fe0ce3bSGanapatrao Kulkarni */ 922*5fe0ce3bSGanapatrao Kulkarni static void __init arm_smmu_v3_set_proximity(struct device *dev, 923*5fe0ce3bSGanapatrao Kulkarni struct acpi_iort_node *node) 924*5fe0ce3bSGanapatrao Kulkarni { 925*5fe0ce3bSGanapatrao Kulkarni struct acpi_iort_smmu_v3 *smmu; 926*5fe0ce3bSGanapatrao Kulkarni 927*5fe0ce3bSGanapatrao Kulkarni smmu = (struct acpi_iort_smmu_v3 *)node->node_data; 928*5fe0ce3bSGanapatrao Kulkarni if (smmu->flags & ACPI_IORT_SMMU_V3_PXM_VALID) { 929*5fe0ce3bSGanapatrao Kulkarni set_dev_node(dev, acpi_map_pxm_to_node(smmu->pxm)); 930*5fe0ce3bSGanapatrao Kulkarni pr_info("SMMU-v3[%llx] Mapped to Proximity domain %d\n", 931*5fe0ce3bSGanapatrao Kulkarni smmu->base_address, 932*5fe0ce3bSGanapatrao Kulkarni smmu->pxm); 933*5fe0ce3bSGanapatrao Kulkarni } 934*5fe0ce3bSGanapatrao Kulkarni } 935*5fe0ce3bSGanapatrao Kulkarni #else 936*5fe0ce3bSGanapatrao Kulkarni #define arm_smmu_v3_set_proximity NULL 937*5fe0ce3bSGanapatrao Kulkarni #endif 938*5fe0ce3bSGanapatrao Kulkarni 939d6fcd3b1SLorenzo Pieralisi static int __init arm_smmu_count_resources(struct acpi_iort_node *node) 940d6fcd3b1SLorenzo Pieralisi { 941d6fcd3b1SLorenzo Pieralisi struct acpi_iort_smmu *smmu; 942d6fcd3b1SLorenzo Pieralisi 943d6fcd3b1SLorenzo Pieralisi /* Retrieve SMMU specific data */ 944d6fcd3b1SLorenzo Pieralisi smmu = (struct acpi_iort_smmu *)node->node_data; 945d6fcd3b1SLorenzo Pieralisi 946d6fcd3b1SLorenzo Pieralisi /* 947d6fcd3b1SLorenzo Pieralisi * Only consider the global fault interrupt and ignore the 948d6fcd3b1SLorenzo Pieralisi * configuration access interrupt. 949d6fcd3b1SLorenzo Pieralisi * 950d6fcd3b1SLorenzo Pieralisi * MMIO address and global fault interrupt resources are always 951d6fcd3b1SLorenzo Pieralisi * present so add them to the context interrupt count as a static 952d6fcd3b1SLorenzo Pieralisi * value. 953d6fcd3b1SLorenzo Pieralisi */ 954d6fcd3b1SLorenzo Pieralisi return smmu->context_interrupt_count + 2; 955d6fcd3b1SLorenzo Pieralisi } 956d6fcd3b1SLorenzo Pieralisi 957d6fcd3b1SLorenzo Pieralisi static void __init arm_smmu_init_resources(struct resource *res, 958d6fcd3b1SLorenzo Pieralisi struct acpi_iort_node *node) 959d6fcd3b1SLorenzo Pieralisi { 960d6fcd3b1SLorenzo Pieralisi struct acpi_iort_smmu *smmu; 961d6fcd3b1SLorenzo Pieralisi int i, hw_irq, trigger, num_res = 0; 962d6fcd3b1SLorenzo Pieralisi u64 *ctx_irq, *glb_irq; 963d6fcd3b1SLorenzo Pieralisi 964d6fcd3b1SLorenzo Pieralisi /* Retrieve SMMU specific data */ 965d6fcd3b1SLorenzo Pieralisi smmu = (struct acpi_iort_smmu *)node->node_data; 966d6fcd3b1SLorenzo Pieralisi 967d6fcd3b1SLorenzo Pieralisi res[num_res].start = smmu->base_address; 968d6fcd3b1SLorenzo Pieralisi res[num_res].end = smmu->base_address + smmu->span - 1; 969d6fcd3b1SLorenzo Pieralisi res[num_res].flags = IORESOURCE_MEM; 970d6fcd3b1SLorenzo Pieralisi num_res++; 971d6fcd3b1SLorenzo Pieralisi 972d6fcd3b1SLorenzo Pieralisi glb_irq = ACPI_ADD_PTR(u64, node, smmu->global_interrupt_offset); 973d6fcd3b1SLorenzo Pieralisi /* Global IRQs */ 974d6fcd3b1SLorenzo Pieralisi hw_irq = IORT_IRQ_MASK(glb_irq[0]); 975d6fcd3b1SLorenzo Pieralisi trigger = IORT_IRQ_TRIGGER_MASK(glb_irq[0]); 976d6fcd3b1SLorenzo Pieralisi 977d6fcd3b1SLorenzo Pieralisi acpi_iort_register_irq(hw_irq, "arm-smmu-global", trigger, 978d6fcd3b1SLorenzo Pieralisi &res[num_res++]); 979d6fcd3b1SLorenzo Pieralisi 980d6fcd3b1SLorenzo Pieralisi /* Context IRQs */ 981d6fcd3b1SLorenzo Pieralisi ctx_irq = ACPI_ADD_PTR(u64, node, smmu->context_interrupt_offset); 982d6fcd3b1SLorenzo Pieralisi for (i = 0; i < smmu->context_interrupt_count; i++) { 983d6fcd3b1SLorenzo Pieralisi hw_irq = IORT_IRQ_MASK(ctx_irq[i]); 984d6fcd3b1SLorenzo Pieralisi trigger = IORT_IRQ_TRIGGER_MASK(ctx_irq[i]); 985d6fcd3b1SLorenzo Pieralisi 986d6fcd3b1SLorenzo Pieralisi acpi_iort_register_irq(hw_irq, "arm-smmu-context", trigger, 987d6fcd3b1SLorenzo Pieralisi &res[num_res++]); 988d6fcd3b1SLorenzo Pieralisi } 989d6fcd3b1SLorenzo Pieralisi } 990d6fcd3b1SLorenzo Pieralisi 991d6fcd3b1SLorenzo Pieralisi static bool __init arm_smmu_is_coherent(struct acpi_iort_node *node) 992d6fcd3b1SLorenzo Pieralisi { 993d6fcd3b1SLorenzo Pieralisi struct acpi_iort_smmu *smmu; 994d6fcd3b1SLorenzo Pieralisi 995d6fcd3b1SLorenzo Pieralisi /* Retrieve SMMU specific data */ 996d6fcd3b1SLorenzo Pieralisi smmu = (struct acpi_iort_smmu *)node->node_data; 997d6fcd3b1SLorenzo Pieralisi 998d6fcd3b1SLorenzo Pieralisi return smmu->flags & ACPI_IORT_SMMU_COHERENT_WALK; 999d6fcd3b1SLorenzo Pieralisi } 1000d6fcd3b1SLorenzo Pieralisi 1001846f0e9eSLorenzo Pieralisi struct iort_iommu_config { 1002846f0e9eSLorenzo Pieralisi const char *name; 1003846f0e9eSLorenzo Pieralisi int (*iommu_init)(struct acpi_iort_node *node); 1004846f0e9eSLorenzo Pieralisi bool (*iommu_is_coherent)(struct acpi_iort_node *node); 1005846f0e9eSLorenzo Pieralisi int (*iommu_count_resources)(struct acpi_iort_node *node); 1006846f0e9eSLorenzo Pieralisi void (*iommu_init_resources)(struct resource *res, 1007846f0e9eSLorenzo Pieralisi struct acpi_iort_node *node); 1008*5fe0ce3bSGanapatrao Kulkarni void (*iommu_set_proximity)(struct device *dev, 1009*5fe0ce3bSGanapatrao Kulkarni struct acpi_iort_node *node); 1010846f0e9eSLorenzo Pieralisi }; 1011846f0e9eSLorenzo Pieralisi 1012e4dadfa8SLorenzo Pieralisi static const struct iort_iommu_config iort_arm_smmu_v3_cfg __initconst = { 1013e4dadfa8SLorenzo Pieralisi .name = "arm-smmu-v3", 1014e4dadfa8SLorenzo Pieralisi .iommu_is_coherent = arm_smmu_v3_is_coherent, 1015e4dadfa8SLorenzo Pieralisi .iommu_count_resources = arm_smmu_v3_count_resources, 1016*5fe0ce3bSGanapatrao Kulkarni .iommu_init_resources = arm_smmu_v3_init_resources, 1017*5fe0ce3bSGanapatrao Kulkarni .iommu_set_proximity = arm_smmu_v3_set_proximity, 1018e4dadfa8SLorenzo Pieralisi }; 1019e4dadfa8SLorenzo Pieralisi 1020d6fcd3b1SLorenzo Pieralisi static const struct iort_iommu_config iort_arm_smmu_cfg __initconst = { 1021d6fcd3b1SLorenzo Pieralisi .name = "arm-smmu", 1022d6fcd3b1SLorenzo Pieralisi .iommu_is_coherent = arm_smmu_is_coherent, 1023d6fcd3b1SLorenzo Pieralisi .iommu_count_resources = arm_smmu_count_resources, 1024d6fcd3b1SLorenzo Pieralisi .iommu_init_resources = arm_smmu_init_resources 1025d6fcd3b1SLorenzo Pieralisi }; 1026d6fcd3b1SLorenzo Pieralisi 1027846f0e9eSLorenzo Pieralisi static __init 1028846f0e9eSLorenzo Pieralisi const struct iort_iommu_config *iort_get_iommu_cfg(struct acpi_iort_node *node) 1029846f0e9eSLorenzo Pieralisi { 1030e4dadfa8SLorenzo Pieralisi switch (node->type) { 1031e4dadfa8SLorenzo Pieralisi case ACPI_IORT_NODE_SMMU_V3: 1032e4dadfa8SLorenzo Pieralisi return &iort_arm_smmu_v3_cfg; 1033d6fcd3b1SLorenzo Pieralisi case ACPI_IORT_NODE_SMMU: 1034d6fcd3b1SLorenzo Pieralisi return &iort_arm_smmu_cfg; 1035e4dadfa8SLorenzo Pieralisi default: 1036846f0e9eSLorenzo Pieralisi return NULL; 1037846f0e9eSLorenzo Pieralisi } 1038e4dadfa8SLorenzo Pieralisi } 1039846f0e9eSLorenzo Pieralisi 1040846f0e9eSLorenzo Pieralisi /** 1041846f0e9eSLorenzo Pieralisi * iort_add_smmu_platform_device() - Allocate a platform device for SMMU 1042846f0e9eSLorenzo Pieralisi * @node: Pointer to SMMU ACPI IORT node 1043846f0e9eSLorenzo Pieralisi * 1044846f0e9eSLorenzo Pieralisi * Returns: 0 on success, <0 failure 1045846f0e9eSLorenzo Pieralisi */ 1046846f0e9eSLorenzo Pieralisi static int __init iort_add_smmu_platform_device(struct acpi_iort_node *node) 1047846f0e9eSLorenzo Pieralisi { 1048846f0e9eSLorenzo Pieralisi struct fwnode_handle *fwnode; 1049846f0e9eSLorenzo Pieralisi struct platform_device *pdev; 1050846f0e9eSLorenzo Pieralisi struct resource *r; 1051846f0e9eSLorenzo Pieralisi enum dev_dma_attr attr; 1052846f0e9eSLorenzo Pieralisi int ret, count; 1053846f0e9eSLorenzo Pieralisi const struct iort_iommu_config *ops = iort_get_iommu_cfg(node); 1054846f0e9eSLorenzo Pieralisi 1055846f0e9eSLorenzo Pieralisi if (!ops) 1056846f0e9eSLorenzo Pieralisi return -ENODEV; 1057846f0e9eSLorenzo Pieralisi 1058846f0e9eSLorenzo Pieralisi pdev = platform_device_alloc(ops->name, PLATFORM_DEVID_AUTO); 1059846f0e9eSLorenzo Pieralisi if (!pdev) 10605e5afa6cSDan Carpenter return -ENOMEM; 1061846f0e9eSLorenzo Pieralisi 1062*5fe0ce3bSGanapatrao Kulkarni if (ops->iommu_set_proximity) 1063*5fe0ce3bSGanapatrao Kulkarni ops->iommu_set_proximity(&pdev->dev, node); 1064*5fe0ce3bSGanapatrao Kulkarni 1065846f0e9eSLorenzo Pieralisi count = ops->iommu_count_resources(node); 1066846f0e9eSLorenzo Pieralisi 1067846f0e9eSLorenzo Pieralisi r = kcalloc(count, sizeof(*r), GFP_KERNEL); 1068846f0e9eSLorenzo Pieralisi if (!r) { 1069846f0e9eSLorenzo Pieralisi ret = -ENOMEM; 1070846f0e9eSLorenzo Pieralisi goto dev_put; 1071846f0e9eSLorenzo Pieralisi } 1072846f0e9eSLorenzo Pieralisi 1073846f0e9eSLorenzo Pieralisi ops->iommu_init_resources(r, node); 1074846f0e9eSLorenzo Pieralisi 1075846f0e9eSLorenzo Pieralisi ret = platform_device_add_resources(pdev, r, count); 1076846f0e9eSLorenzo Pieralisi /* 1077846f0e9eSLorenzo Pieralisi * Resources are duplicated in platform_device_add_resources, 1078846f0e9eSLorenzo Pieralisi * free their allocated memory 1079846f0e9eSLorenzo Pieralisi */ 1080846f0e9eSLorenzo Pieralisi kfree(r); 1081846f0e9eSLorenzo Pieralisi 1082846f0e9eSLorenzo Pieralisi if (ret) 1083846f0e9eSLorenzo Pieralisi goto dev_put; 1084846f0e9eSLorenzo Pieralisi 1085846f0e9eSLorenzo Pieralisi /* 1086846f0e9eSLorenzo Pieralisi * Add a copy of IORT node pointer to platform_data to 1087846f0e9eSLorenzo Pieralisi * be used to retrieve IORT data information. 1088846f0e9eSLorenzo Pieralisi */ 1089846f0e9eSLorenzo Pieralisi ret = platform_device_add_data(pdev, &node, sizeof(node)); 1090846f0e9eSLorenzo Pieralisi if (ret) 1091846f0e9eSLorenzo Pieralisi goto dev_put; 1092846f0e9eSLorenzo Pieralisi 1093846f0e9eSLorenzo Pieralisi /* 1094846f0e9eSLorenzo Pieralisi * We expect the dma masks to be equivalent for 1095846f0e9eSLorenzo Pieralisi * all SMMUs set-ups 1096846f0e9eSLorenzo Pieralisi */ 1097846f0e9eSLorenzo Pieralisi pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask; 1098846f0e9eSLorenzo Pieralisi 1099846f0e9eSLorenzo Pieralisi fwnode = iort_get_fwnode(node); 1100846f0e9eSLorenzo Pieralisi 1101846f0e9eSLorenzo Pieralisi if (!fwnode) { 1102846f0e9eSLorenzo Pieralisi ret = -ENODEV; 1103846f0e9eSLorenzo Pieralisi goto dev_put; 1104846f0e9eSLorenzo Pieralisi } 1105846f0e9eSLorenzo Pieralisi 1106846f0e9eSLorenzo Pieralisi pdev->dev.fwnode = fwnode; 1107846f0e9eSLorenzo Pieralisi 1108846f0e9eSLorenzo Pieralisi attr = ops->iommu_is_coherent(node) ? 1109846f0e9eSLorenzo Pieralisi DEV_DMA_COHERENT : DEV_DMA_NON_COHERENT; 1110846f0e9eSLorenzo Pieralisi 1111846f0e9eSLorenzo Pieralisi /* Configure DMA for the page table walker */ 1112846f0e9eSLorenzo Pieralisi acpi_dma_configure(&pdev->dev, attr); 1113846f0e9eSLorenzo Pieralisi 1114846f0e9eSLorenzo Pieralisi ret = platform_device_add(pdev); 1115846f0e9eSLorenzo Pieralisi if (ret) 1116846f0e9eSLorenzo Pieralisi goto dma_deconfigure; 1117846f0e9eSLorenzo Pieralisi 1118846f0e9eSLorenzo Pieralisi return 0; 1119846f0e9eSLorenzo Pieralisi 1120846f0e9eSLorenzo Pieralisi dma_deconfigure: 1121846f0e9eSLorenzo Pieralisi acpi_dma_deconfigure(&pdev->dev); 1122846f0e9eSLorenzo Pieralisi dev_put: 1123846f0e9eSLorenzo Pieralisi platform_device_put(pdev); 1124846f0e9eSLorenzo Pieralisi 1125846f0e9eSLorenzo Pieralisi return ret; 1126846f0e9eSLorenzo Pieralisi } 1127846f0e9eSLorenzo Pieralisi 1128846f0e9eSLorenzo Pieralisi static void __init iort_init_platform_devices(void) 1129846f0e9eSLorenzo Pieralisi { 1130846f0e9eSLorenzo Pieralisi struct acpi_iort_node *iort_node, *iort_end; 1131846f0e9eSLorenzo Pieralisi struct acpi_table_iort *iort; 1132846f0e9eSLorenzo Pieralisi struct fwnode_handle *fwnode; 1133846f0e9eSLorenzo Pieralisi int i, ret; 1134846f0e9eSLorenzo Pieralisi 1135846f0e9eSLorenzo Pieralisi /* 1136846f0e9eSLorenzo Pieralisi * iort_table and iort both point to the start of IORT table, but 1137846f0e9eSLorenzo Pieralisi * have different struct types 1138846f0e9eSLorenzo Pieralisi */ 1139846f0e9eSLorenzo Pieralisi iort = (struct acpi_table_iort *)iort_table; 1140846f0e9eSLorenzo Pieralisi 1141846f0e9eSLorenzo Pieralisi /* Get the first IORT node */ 1142846f0e9eSLorenzo Pieralisi iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort, 1143846f0e9eSLorenzo Pieralisi iort->node_offset); 1144846f0e9eSLorenzo Pieralisi iort_end = ACPI_ADD_PTR(struct acpi_iort_node, iort, 1145846f0e9eSLorenzo Pieralisi iort_table->length); 1146846f0e9eSLorenzo Pieralisi 1147846f0e9eSLorenzo Pieralisi for (i = 0; i < iort->node_count; i++) { 1148846f0e9eSLorenzo Pieralisi if (iort_node >= iort_end) { 1149846f0e9eSLorenzo Pieralisi pr_err("iort node pointer overflows, bad table\n"); 1150846f0e9eSLorenzo Pieralisi return; 1151846f0e9eSLorenzo Pieralisi } 1152846f0e9eSLorenzo Pieralisi 1153846f0e9eSLorenzo Pieralisi if ((iort_node->type == ACPI_IORT_NODE_SMMU) || 1154846f0e9eSLorenzo Pieralisi (iort_node->type == ACPI_IORT_NODE_SMMU_V3)) { 1155846f0e9eSLorenzo Pieralisi 1156846f0e9eSLorenzo Pieralisi fwnode = acpi_alloc_fwnode_static(); 1157846f0e9eSLorenzo Pieralisi if (!fwnode) 1158846f0e9eSLorenzo Pieralisi return; 1159846f0e9eSLorenzo Pieralisi 1160846f0e9eSLorenzo Pieralisi iort_set_fwnode(iort_node, fwnode); 1161846f0e9eSLorenzo Pieralisi 1162846f0e9eSLorenzo Pieralisi ret = iort_add_smmu_platform_device(iort_node); 1163846f0e9eSLorenzo Pieralisi if (ret) { 1164846f0e9eSLorenzo Pieralisi iort_delete_fwnode(iort_node); 1165846f0e9eSLorenzo Pieralisi acpi_free_fwnode_static(fwnode); 1166846f0e9eSLorenzo Pieralisi return; 1167846f0e9eSLorenzo Pieralisi } 1168846f0e9eSLorenzo Pieralisi } 1169846f0e9eSLorenzo Pieralisi 1170846f0e9eSLorenzo Pieralisi iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort_node, 1171846f0e9eSLorenzo Pieralisi iort_node->length); 1172846f0e9eSLorenzo Pieralisi } 1173846f0e9eSLorenzo Pieralisi } 1174846f0e9eSLorenzo Pieralisi 117588ef16d8STomasz Nowicki void __init acpi_iort_init(void) 117688ef16d8STomasz Nowicki { 117788ef16d8STomasz Nowicki acpi_status status; 117888ef16d8STomasz Nowicki 117988ef16d8STomasz Nowicki status = acpi_get_table(ACPI_SIG_IORT, 0, &iort_table); 118034ceea27SLorenzo Pieralisi if (ACPI_FAILURE(status)) { 118134ceea27SLorenzo Pieralisi if (status != AE_NOT_FOUND) { 118288ef16d8STomasz Nowicki const char *msg = acpi_format_exception(status); 118334ceea27SLorenzo Pieralisi 118488ef16d8STomasz Nowicki pr_err("Failed to get table, %s\n", msg); 118588ef16d8STomasz Nowicki } 118634ceea27SLorenzo Pieralisi 118734ceea27SLorenzo Pieralisi return; 118834ceea27SLorenzo Pieralisi } 118934ceea27SLorenzo Pieralisi 1190846f0e9eSLorenzo Pieralisi iort_init_platform_devices(); 119188ef16d8STomasz Nowicki } 1192