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 591643b8e4dSLorenzo Pieralisi static int __get_pci_rid(struct pci_dev *pdev, u16 alias, void *data) 592643b8e4dSLorenzo Pieralisi { 593643b8e4dSLorenzo Pieralisi u32 *rid = data; 594643b8e4dSLorenzo Pieralisi 595643b8e4dSLorenzo Pieralisi *rid = alias; 596643b8e4dSLorenzo Pieralisi return 0; 597643b8e4dSLorenzo Pieralisi } 598643b8e4dSLorenzo Pieralisi 599643b8e4dSLorenzo Pieralisi static int arm_smmu_iort_xlate(struct device *dev, u32 streamid, 600643b8e4dSLorenzo Pieralisi struct fwnode_handle *fwnode, 601643b8e4dSLorenzo Pieralisi const struct iommu_ops *ops) 602643b8e4dSLorenzo Pieralisi { 603643b8e4dSLorenzo Pieralisi int ret = iommu_fwspec_init(dev, fwnode, ops); 604643b8e4dSLorenzo Pieralisi 605643b8e4dSLorenzo Pieralisi if (!ret) 606643b8e4dSLorenzo Pieralisi ret = iommu_fwspec_add_ids(dev, &streamid, 1); 607643b8e4dSLorenzo Pieralisi 608643b8e4dSLorenzo Pieralisi return ret; 609643b8e4dSLorenzo Pieralisi } 610643b8e4dSLorenzo Pieralisi 6111d9029d4SLorenzo Pieralisi static inline bool iort_iommu_driver_enabled(u8 type) 6121d9029d4SLorenzo Pieralisi { 6131d9029d4SLorenzo Pieralisi switch (type) { 6141d9029d4SLorenzo Pieralisi case ACPI_IORT_NODE_SMMU_V3: 6151d9029d4SLorenzo Pieralisi return IS_BUILTIN(CONFIG_ARM_SMMU_V3); 6161d9029d4SLorenzo Pieralisi case ACPI_IORT_NODE_SMMU: 6171d9029d4SLorenzo Pieralisi return IS_BUILTIN(CONFIG_ARM_SMMU); 6181d9029d4SLorenzo Pieralisi default: 6191d9029d4SLorenzo Pieralisi pr_warn("IORT node type %u does not describe an SMMU\n", type); 6201d9029d4SLorenzo Pieralisi return false; 6211d9029d4SLorenzo Pieralisi } 6221d9029d4SLorenzo Pieralisi } 6231d9029d4SLorenzo Pieralisi 624d49f2dedSLorenzo Pieralisi #ifdef CONFIG_IOMMU_API 625d49f2dedSLorenzo Pieralisi static inline 626d49f2dedSLorenzo Pieralisi const struct iommu_ops *iort_fwspec_iommu_ops(struct iommu_fwspec *fwspec) 627d49f2dedSLorenzo Pieralisi { 628d49f2dedSLorenzo Pieralisi return (fwspec && fwspec->ops) ? fwspec->ops : NULL; 629d49f2dedSLorenzo Pieralisi } 630d49f2dedSLorenzo Pieralisi 631d49f2dedSLorenzo Pieralisi static inline 632d49f2dedSLorenzo Pieralisi int iort_add_device_replay(const struct iommu_ops *ops, struct device *dev) 633d49f2dedSLorenzo Pieralisi { 634d49f2dedSLorenzo Pieralisi int err = 0; 635d49f2dedSLorenzo Pieralisi 636d49f2dedSLorenzo Pieralisi if (!IS_ERR_OR_NULL(ops) && ops->add_device && dev->bus && 637d49f2dedSLorenzo Pieralisi !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 651643b8e4dSLorenzo Pieralisi static const struct iommu_ops *iort_iommu_xlate(struct device *dev, 652643b8e4dSLorenzo Pieralisi struct acpi_iort_node *node, 653643b8e4dSLorenzo Pieralisi u32 streamid) 654643b8e4dSLorenzo Pieralisi { 655643b8e4dSLorenzo Pieralisi const struct iommu_ops *ops = NULL; 656643b8e4dSLorenzo Pieralisi int ret = -ENODEV; 657643b8e4dSLorenzo Pieralisi struct fwnode_handle *iort_fwnode; 658643b8e4dSLorenzo Pieralisi 659643b8e4dSLorenzo Pieralisi if (node) { 660643b8e4dSLorenzo Pieralisi iort_fwnode = iort_get_fwnode(node); 661643b8e4dSLorenzo Pieralisi if (!iort_fwnode) 662643b8e4dSLorenzo Pieralisi return NULL; 663643b8e4dSLorenzo Pieralisi 664534766dfSJoerg Roedel ops = iommu_ops_from_fwnode(iort_fwnode); 6655a1bb638SSricharan R /* 6665a1bb638SSricharan R * If the ops look-up fails, this means that either 6675a1bb638SSricharan R * the SMMU drivers have not been probed yet or that 6685a1bb638SSricharan R * the SMMU drivers are not built in the kernel; 6695a1bb638SSricharan R * Depending on whether the SMMU drivers are built-in 6705a1bb638SSricharan R * in the kernel or not, defer the IOMMU configuration 6715a1bb638SSricharan R * or just abort it. 6725a1bb638SSricharan R */ 673643b8e4dSLorenzo Pieralisi if (!ops) 6745a1bb638SSricharan R return iort_iommu_driver_enabled(node->type) ? 6755a1bb638SSricharan R ERR_PTR(-EPROBE_DEFER) : NULL; 676643b8e4dSLorenzo Pieralisi 677643b8e4dSLorenzo Pieralisi ret = arm_smmu_iort_xlate(dev, streamid, iort_fwnode, ops); 678643b8e4dSLorenzo Pieralisi } 679643b8e4dSLorenzo Pieralisi 680643b8e4dSLorenzo Pieralisi return ret ? NULL : ops; 681643b8e4dSLorenzo Pieralisi } 682643b8e4dSLorenzo Pieralisi 683*10d8ab2cSLorenzo Pieralisi static int nc_dma_get_range(struct device *dev, u64 *size) 684*10d8ab2cSLorenzo Pieralisi { 685*10d8ab2cSLorenzo Pieralisi struct acpi_iort_node *node; 686*10d8ab2cSLorenzo Pieralisi struct acpi_iort_named_component *ncomp; 687*10d8ab2cSLorenzo Pieralisi 688*10d8ab2cSLorenzo Pieralisi node = iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT, 689*10d8ab2cSLorenzo Pieralisi iort_match_node_callback, dev); 690*10d8ab2cSLorenzo Pieralisi if (!node) 691*10d8ab2cSLorenzo Pieralisi return -ENODEV; 692*10d8ab2cSLorenzo Pieralisi 693*10d8ab2cSLorenzo Pieralisi ncomp = (struct acpi_iort_named_component *)node->node_data; 694*10d8ab2cSLorenzo Pieralisi 695*10d8ab2cSLorenzo Pieralisi *size = ncomp->memory_address_limit >= 64 ? U64_MAX : 696*10d8ab2cSLorenzo Pieralisi 1ULL<<ncomp->memory_address_limit; 697*10d8ab2cSLorenzo Pieralisi 698*10d8ab2cSLorenzo Pieralisi return 0; 699*10d8ab2cSLorenzo Pieralisi } 700*10d8ab2cSLorenzo Pieralisi 701643b8e4dSLorenzo Pieralisi /** 7027ad42639SLorenzo Pieralisi * iort_dma_setup() - Set-up device DMA parameters. 70318b709beSLorenzo Pieralisi * 70418b709beSLorenzo Pieralisi * @dev: device to configure 7057ad42639SLorenzo Pieralisi * @dma_addr: device DMA address result pointer 7067ad42639SLorenzo Pieralisi * @size: DMA range size result pointer 70718b709beSLorenzo Pieralisi */ 7087ad42639SLorenzo Pieralisi void iort_dma_setup(struct device *dev, u64 *dma_addr, u64 *dma_size) 70918b709beSLorenzo Pieralisi { 7107ad42639SLorenzo Pieralisi u64 mask, dmaaddr = 0, size = 0, offset = 0; 7117ad42639SLorenzo Pieralisi int ret, msb; 7127ad42639SLorenzo Pieralisi 71318b709beSLorenzo Pieralisi /* 71418b709beSLorenzo Pieralisi * Set default coherent_dma_mask to 32 bit. Drivers are expected to 71518b709beSLorenzo Pieralisi * setup the correct supported mask. 71618b709beSLorenzo Pieralisi */ 71718b709beSLorenzo Pieralisi if (!dev->coherent_dma_mask) 71818b709beSLorenzo Pieralisi dev->coherent_dma_mask = DMA_BIT_MASK(32); 71918b709beSLorenzo Pieralisi 72018b709beSLorenzo Pieralisi /* 72118b709beSLorenzo Pieralisi * Set it to coherent_dma_mask by default if the architecture 72218b709beSLorenzo Pieralisi * code has not set it. 72318b709beSLorenzo Pieralisi */ 72418b709beSLorenzo Pieralisi if (!dev->dma_mask) 72518b709beSLorenzo Pieralisi dev->dma_mask = &dev->coherent_dma_mask; 7267ad42639SLorenzo Pieralisi 7277ad42639SLorenzo Pieralisi size = max(dev->coherent_dma_mask, dev->coherent_dma_mask + 1); 7287ad42639SLorenzo Pieralisi 729*10d8ab2cSLorenzo Pieralisi if (dev_is_pci(dev)) 7307ad42639SLorenzo Pieralisi ret = acpi_dma_get_range(dev, &dmaaddr, &offset, &size); 731*10d8ab2cSLorenzo Pieralisi else 732*10d8ab2cSLorenzo Pieralisi ret = nc_dma_get_range(dev, &size); 733*10d8ab2cSLorenzo Pieralisi 7347ad42639SLorenzo Pieralisi if (!ret) { 7357ad42639SLorenzo Pieralisi msb = fls64(dmaaddr + size - 1); 7367ad42639SLorenzo Pieralisi /* 7377ad42639SLorenzo Pieralisi * Round-up to the power-of-two mask or set 7387ad42639SLorenzo Pieralisi * the mask to the whole 64-bit address space 7397ad42639SLorenzo Pieralisi * in case the DMA region covers the full 7407ad42639SLorenzo Pieralisi * memory window. 7417ad42639SLorenzo Pieralisi */ 7427ad42639SLorenzo Pieralisi mask = msb == 64 ? U64_MAX : (1ULL << msb) - 1; 7437ad42639SLorenzo Pieralisi /* 7447ad42639SLorenzo Pieralisi * Limit coherent and dma mask based on size 7457ad42639SLorenzo Pieralisi * retrieved from firmware. 7467ad42639SLorenzo Pieralisi */ 7477ad42639SLorenzo Pieralisi dev->coherent_dma_mask = mask; 7487ad42639SLorenzo Pieralisi *dev->dma_mask = mask; 7497ad42639SLorenzo Pieralisi } 7507ad42639SLorenzo Pieralisi 7517ad42639SLorenzo Pieralisi *dma_addr = dmaaddr; 7527ad42639SLorenzo Pieralisi *dma_size = size; 7537ad42639SLorenzo Pieralisi 7547ad42639SLorenzo Pieralisi dev->dma_pfn_offset = PFN_DOWN(offset); 7557ad42639SLorenzo Pieralisi dev_dbg(dev, "dma_pfn_offset(%#08llx)\n", offset); 75618b709beSLorenzo Pieralisi } 75718b709beSLorenzo Pieralisi 75818b709beSLorenzo Pieralisi /** 759643b8e4dSLorenzo Pieralisi * iort_iommu_configure - Set-up IOMMU configuration for a device. 760643b8e4dSLorenzo Pieralisi * 761643b8e4dSLorenzo Pieralisi * @dev: device to configure 762643b8e4dSLorenzo Pieralisi * 763643b8e4dSLorenzo Pieralisi * Returns: iommu_ops pointer on configuration success 764643b8e4dSLorenzo Pieralisi * NULL on configuration failure 765643b8e4dSLorenzo Pieralisi */ 766643b8e4dSLorenzo Pieralisi const struct iommu_ops *iort_iommu_configure(struct device *dev) 767643b8e4dSLorenzo Pieralisi { 768643b8e4dSLorenzo Pieralisi struct acpi_iort_node *node, *parent; 769643b8e4dSLorenzo Pieralisi const struct iommu_ops *ops = NULL; 770643b8e4dSLorenzo Pieralisi u32 streamid = 0; 771d49f2dedSLorenzo Pieralisi int err; 772643b8e4dSLorenzo Pieralisi 7734dac3210SLorenzo Pieralisi /* 7744dac3210SLorenzo Pieralisi * If we already translated the fwspec there 7754dac3210SLorenzo Pieralisi * is nothing left to do, return the iommu_ops. 7764dac3210SLorenzo Pieralisi */ 7774dac3210SLorenzo Pieralisi ops = iort_fwspec_iommu_ops(dev->iommu_fwspec); 7784dac3210SLorenzo Pieralisi if (ops) 7794dac3210SLorenzo Pieralisi return ops; 7804dac3210SLorenzo Pieralisi 781643b8e4dSLorenzo Pieralisi if (dev_is_pci(dev)) { 782643b8e4dSLorenzo Pieralisi struct pci_bus *bus = to_pci_dev(dev)->bus; 783643b8e4dSLorenzo Pieralisi u32 rid; 784643b8e4dSLorenzo Pieralisi 785643b8e4dSLorenzo Pieralisi pci_for_each_dma_alias(to_pci_dev(dev), __get_pci_rid, 786643b8e4dSLorenzo Pieralisi &rid); 787643b8e4dSLorenzo Pieralisi 788643b8e4dSLorenzo Pieralisi node = iort_scan_node(ACPI_IORT_NODE_PCI_ROOT_COMPLEX, 789643b8e4dSLorenzo Pieralisi iort_match_node_callback, &bus->dev); 790643b8e4dSLorenzo Pieralisi if (!node) 791643b8e4dSLorenzo Pieralisi return NULL; 792643b8e4dSLorenzo Pieralisi 793697f6093SHanjun Guo parent = iort_node_map_id(node, rid, &streamid, 794643b8e4dSLorenzo Pieralisi IORT_IOMMU_TYPE); 795643b8e4dSLorenzo Pieralisi 796643b8e4dSLorenzo Pieralisi ops = iort_iommu_xlate(dev, parent, streamid); 797643b8e4dSLorenzo Pieralisi 798643b8e4dSLorenzo Pieralisi } else { 799643b8e4dSLorenzo Pieralisi int i = 0; 800643b8e4dSLorenzo Pieralisi 801643b8e4dSLorenzo Pieralisi node = iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT, 802643b8e4dSLorenzo Pieralisi iort_match_node_callback, dev); 803643b8e4dSLorenzo Pieralisi if (!node) 804643b8e4dSLorenzo Pieralisi return NULL; 805643b8e4dSLorenzo Pieralisi 8068ca4f1d3SHanjun Guo parent = iort_node_map_platform_id(node, &streamid, 807643b8e4dSLorenzo Pieralisi IORT_IOMMU_TYPE, i++); 808643b8e4dSLorenzo Pieralisi 809643b8e4dSLorenzo Pieralisi while (parent) { 810643b8e4dSLorenzo Pieralisi ops = iort_iommu_xlate(dev, parent, streamid); 8115a1bb638SSricharan R if (IS_ERR_OR_NULL(ops)) 8125a1bb638SSricharan R return ops; 813643b8e4dSLorenzo Pieralisi 8148ca4f1d3SHanjun Guo parent = iort_node_map_platform_id(node, &streamid, 8158ca4f1d3SHanjun Guo IORT_IOMMU_TYPE, 8168ca4f1d3SHanjun Guo i++); 817643b8e4dSLorenzo Pieralisi } 818643b8e4dSLorenzo Pieralisi } 819643b8e4dSLorenzo Pieralisi 8205a1bb638SSricharan R /* 8215a1bb638SSricharan R * If we have reason to believe the IOMMU driver missed the initial 8225a1bb638SSricharan R * add_device callback for dev, replay it to get things in order. 8235a1bb638SSricharan R */ 824d49f2dedSLorenzo Pieralisi err = iort_add_device_replay(ops, dev); 8255a1bb638SSricharan R if (err) 8265a1bb638SSricharan R ops = ERR_PTR(err); 8275a1bb638SSricharan R 828058f8c3fSSricharan R /* Ignore all other errors apart from EPROBE_DEFER */ 829058f8c3fSSricharan R if (IS_ERR(ops) && (PTR_ERR(ops) != -EPROBE_DEFER)) { 830058f8c3fSSricharan R dev_dbg(dev, "Adding to IOMMU failed: %ld\n", PTR_ERR(ops)); 831058f8c3fSSricharan R ops = NULL; 832058f8c3fSSricharan R } 833058f8c3fSSricharan R 834643b8e4dSLorenzo Pieralisi return ops; 835643b8e4dSLorenzo Pieralisi } 836643b8e4dSLorenzo Pieralisi 837e4dadfa8SLorenzo Pieralisi static void __init acpi_iort_register_irq(int hwirq, const char *name, 838e4dadfa8SLorenzo Pieralisi int trigger, 839e4dadfa8SLorenzo Pieralisi struct resource *res) 840e4dadfa8SLorenzo Pieralisi { 841e4dadfa8SLorenzo Pieralisi int irq = acpi_register_gsi(NULL, hwirq, trigger, 842e4dadfa8SLorenzo Pieralisi ACPI_ACTIVE_HIGH); 843e4dadfa8SLorenzo Pieralisi 844e4dadfa8SLorenzo Pieralisi if (irq <= 0) { 845e4dadfa8SLorenzo Pieralisi pr_err("could not register gsi hwirq %d name [%s]\n", hwirq, 846e4dadfa8SLorenzo Pieralisi name); 847e4dadfa8SLorenzo Pieralisi return; 848e4dadfa8SLorenzo Pieralisi } 849e4dadfa8SLorenzo Pieralisi 850e4dadfa8SLorenzo Pieralisi res->start = irq; 851e4dadfa8SLorenzo Pieralisi res->end = irq; 852e4dadfa8SLorenzo Pieralisi res->flags = IORESOURCE_IRQ; 853e4dadfa8SLorenzo Pieralisi res->name = name; 854e4dadfa8SLorenzo Pieralisi } 855e4dadfa8SLorenzo Pieralisi 856e4dadfa8SLorenzo Pieralisi static int __init arm_smmu_v3_count_resources(struct acpi_iort_node *node) 857e4dadfa8SLorenzo Pieralisi { 858e4dadfa8SLorenzo Pieralisi struct acpi_iort_smmu_v3 *smmu; 859e4dadfa8SLorenzo Pieralisi /* Always present mem resource */ 860e4dadfa8SLorenzo Pieralisi int num_res = 1; 861e4dadfa8SLorenzo Pieralisi 862e4dadfa8SLorenzo Pieralisi /* Retrieve SMMUv3 specific data */ 863e4dadfa8SLorenzo Pieralisi smmu = (struct acpi_iort_smmu_v3 *)node->node_data; 864e4dadfa8SLorenzo Pieralisi 865e4dadfa8SLorenzo Pieralisi if (smmu->event_gsiv) 866e4dadfa8SLorenzo Pieralisi num_res++; 867e4dadfa8SLorenzo Pieralisi 868e4dadfa8SLorenzo Pieralisi if (smmu->pri_gsiv) 869e4dadfa8SLorenzo Pieralisi num_res++; 870e4dadfa8SLorenzo Pieralisi 871e4dadfa8SLorenzo Pieralisi if (smmu->gerr_gsiv) 872e4dadfa8SLorenzo Pieralisi num_res++; 873e4dadfa8SLorenzo Pieralisi 874e4dadfa8SLorenzo Pieralisi if (smmu->sync_gsiv) 875e4dadfa8SLorenzo Pieralisi num_res++; 876e4dadfa8SLorenzo Pieralisi 877e4dadfa8SLorenzo Pieralisi return num_res; 878e4dadfa8SLorenzo Pieralisi } 879e4dadfa8SLorenzo Pieralisi 880f935448aSGeetha Sowjanya static bool arm_smmu_v3_is_combined_irq(struct acpi_iort_smmu_v3 *smmu) 881f935448aSGeetha Sowjanya { 882f935448aSGeetha Sowjanya /* 883f935448aSGeetha Sowjanya * Cavium ThunderX2 implementation doesn't not support unique 884f935448aSGeetha Sowjanya * irq line. Use single irq line for all the SMMUv3 interrupts. 885f935448aSGeetha Sowjanya */ 886f935448aSGeetha Sowjanya if (smmu->model != ACPI_IORT_SMMU_V3_CAVIUM_CN99XX) 887f935448aSGeetha Sowjanya return false; 888f935448aSGeetha Sowjanya 889f935448aSGeetha Sowjanya /* 890f935448aSGeetha Sowjanya * ThunderX2 doesn't support MSIs from the SMMU, so we're checking 891f935448aSGeetha Sowjanya * SPI numbers here. 892f935448aSGeetha Sowjanya */ 893f935448aSGeetha Sowjanya return smmu->event_gsiv == smmu->pri_gsiv && 894f935448aSGeetha Sowjanya smmu->event_gsiv == smmu->gerr_gsiv && 895f935448aSGeetha Sowjanya smmu->event_gsiv == smmu->sync_gsiv; 896f935448aSGeetha Sowjanya } 897f935448aSGeetha Sowjanya 898403e8c7cSLinu Cherian static unsigned long arm_smmu_v3_resource_size(struct acpi_iort_smmu_v3 *smmu) 899403e8c7cSLinu Cherian { 900403e8c7cSLinu Cherian /* 901403e8c7cSLinu Cherian * Override the size, for Cavium ThunderX2 implementation 902403e8c7cSLinu Cherian * which doesn't support the page 1 SMMU register space. 903403e8c7cSLinu Cherian */ 904403e8c7cSLinu Cherian if (smmu->model == ACPI_IORT_SMMU_V3_CAVIUM_CN99XX) 905403e8c7cSLinu Cherian return SZ_64K; 906403e8c7cSLinu Cherian 907403e8c7cSLinu Cherian return SZ_128K; 908403e8c7cSLinu Cherian } 909403e8c7cSLinu Cherian 910e4dadfa8SLorenzo Pieralisi static void __init arm_smmu_v3_init_resources(struct resource *res, 911e4dadfa8SLorenzo Pieralisi struct acpi_iort_node *node) 912e4dadfa8SLorenzo Pieralisi { 913e4dadfa8SLorenzo Pieralisi struct acpi_iort_smmu_v3 *smmu; 914e4dadfa8SLorenzo Pieralisi int num_res = 0; 915e4dadfa8SLorenzo Pieralisi 916e4dadfa8SLorenzo Pieralisi /* Retrieve SMMUv3 specific data */ 917e4dadfa8SLorenzo Pieralisi smmu = (struct acpi_iort_smmu_v3 *)node->node_data; 918e4dadfa8SLorenzo Pieralisi 919e4dadfa8SLorenzo Pieralisi res[num_res].start = smmu->base_address; 920403e8c7cSLinu Cherian res[num_res].end = smmu->base_address + 921403e8c7cSLinu Cherian arm_smmu_v3_resource_size(smmu) - 1; 922e4dadfa8SLorenzo Pieralisi res[num_res].flags = IORESOURCE_MEM; 923e4dadfa8SLorenzo Pieralisi 924e4dadfa8SLorenzo Pieralisi num_res++; 925f935448aSGeetha Sowjanya if (arm_smmu_v3_is_combined_irq(smmu)) { 926f935448aSGeetha Sowjanya if (smmu->event_gsiv) 927f935448aSGeetha Sowjanya acpi_iort_register_irq(smmu->event_gsiv, "combined", 928f935448aSGeetha Sowjanya ACPI_EDGE_SENSITIVE, 929f935448aSGeetha Sowjanya &res[num_res++]); 930f935448aSGeetha Sowjanya } else { 931e4dadfa8SLorenzo Pieralisi 932e4dadfa8SLorenzo Pieralisi if (smmu->event_gsiv) 933e4dadfa8SLorenzo Pieralisi acpi_iort_register_irq(smmu->event_gsiv, "eventq", 934e4dadfa8SLorenzo Pieralisi ACPI_EDGE_SENSITIVE, 935e4dadfa8SLorenzo Pieralisi &res[num_res++]); 936e4dadfa8SLorenzo Pieralisi 937e4dadfa8SLorenzo Pieralisi if (smmu->pri_gsiv) 938e4dadfa8SLorenzo Pieralisi acpi_iort_register_irq(smmu->pri_gsiv, "priq", 939e4dadfa8SLorenzo Pieralisi ACPI_EDGE_SENSITIVE, 940e4dadfa8SLorenzo Pieralisi &res[num_res++]); 941e4dadfa8SLorenzo Pieralisi 942e4dadfa8SLorenzo Pieralisi if (smmu->gerr_gsiv) 943e4dadfa8SLorenzo Pieralisi acpi_iort_register_irq(smmu->gerr_gsiv, "gerror", 944e4dadfa8SLorenzo Pieralisi ACPI_EDGE_SENSITIVE, 945e4dadfa8SLorenzo Pieralisi &res[num_res++]); 946e4dadfa8SLorenzo Pieralisi 947e4dadfa8SLorenzo Pieralisi if (smmu->sync_gsiv) 948e4dadfa8SLorenzo Pieralisi acpi_iort_register_irq(smmu->sync_gsiv, "cmdq-sync", 949e4dadfa8SLorenzo Pieralisi ACPI_EDGE_SENSITIVE, 950e4dadfa8SLorenzo Pieralisi &res[num_res++]); 951e4dadfa8SLorenzo Pieralisi } 952f935448aSGeetha Sowjanya } 953e4dadfa8SLorenzo Pieralisi 954e4dadfa8SLorenzo Pieralisi static bool __init arm_smmu_v3_is_coherent(struct acpi_iort_node *node) 955e4dadfa8SLorenzo Pieralisi { 956e4dadfa8SLorenzo Pieralisi struct acpi_iort_smmu_v3 *smmu; 957e4dadfa8SLorenzo Pieralisi 958e4dadfa8SLorenzo Pieralisi /* Retrieve SMMUv3 specific data */ 959e4dadfa8SLorenzo Pieralisi smmu = (struct acpi_iort_smmu_v3 *)node->node_data; 960e4dadfa8SLorenzo Pieralisi 961e4dadfa8SLorenzo Pieralisi return smmu->flags & ACPI_IORT_SMMU_V3_COHACC_OVERRIDE; 962e4dadfa8SLorenzo Pieralisi } 963e4dadfa8SLorenzo Pieralisi 964d6fcd3b1SLorenzo Pieralisi static int __init arm_smmu_count_resources(struct acpi_iort_node *node) 965d6fcd3b1SLorenzo Pieralisi { 966d6fcd3b1SLorenzo Pieralisi struct acpi_iort_smmu *smmu; 967d6fcd3b1SLorenzo Pieralisi 968d6fcd3b1SLorenzo Pieralisi /* Retrieve SMMU specific data */ 969d6fcd3b1SLorenzo Pieralisi smmu = (struct acpi_iort_smmu *)node->node_data; 970d6fcd3b1SLorenzo Pieralisi 971d6fcd3b1SLorenzo Pieralisi /* 972d6fcd3b1SLorenzo Pieralisi * Only consider the global fault interrupt and ignore the 973d6fcd3b1SLorenzo Pieralisi * configuration access interrupt. 974d6fcd3b1SLorenzo Pieralisi * 975d6fcd3b1SLorenzo Pieralisi * MMIO address and global fault interrupt resources are always 976d6fcd3b1SLorenzo Pieralisi * present so add them to the context interrupt count as a static 977d6fcd3b1SLorenzo Pieralisi * value. 978d6fcd3b1SLorenzo Pieralisi */ 979d6fcd3b1SLorenzo Pieralisi return smmu->context_interrupt_count + 2; 980d6fcd3b1SLorenzo Pieralisi } 981d6fcd3b1SLorenzo Pieralisi 982d6fcd3b1SLorenzo Pieralisi static void __init arm_smmu_init_resources(struct resource *res, 983d6fcd3b1SLorenzo Pieralisi struct acpi_iort_node *node) 984d6fcd3b1SLorenzo Pieralisi { 985d6fcd3b1SLorenzo Pieralisi struct acpi_iort_smmu *smmu; 986d6fcd3b1SLorenzo Pieralisi int i, hw_irq, trigger, num_res = 0; 987d6fcd3b1SLorenzo Pieralisi u64 *ctx_irq, *glb_irq; 988d6fcd3b1SLorenzo Pieralisi 989d6fcd3b1SLorenzo Pieralisi /* Retrieve SMMU specific data */ 990d6fcd3b1SLorenzo Pieralisi smmu = (struct acpi_iort_smmu *)node->node_data; 991d6fcd3b1SLorenzo Pieralisi 992d6fcd3b1SLorenzo Pieralisi res[num_res].start = smmu->base_address; 993d6fcd3b1SLorenzo Pieralisi res[num_res].end = smmu->base_address + smmu->span - 1; 994d6fcd3b1SLorenzo Pieralisi res[num_res].flags = IORESOURCE_MEM; 995d6fcd3b1SLorenzo Pieralisi num_res++; 996d6fcd3b1SLorenzo Pieralisi 997d6fcd3b1SLorenzo Pieralisi glb_irq = ACPI_ADD_PTR(u64, node, smmu->global_interrupt_offset); 998d6fcd3b1SLorenzo Pieralisi /* Global IRQs */ 999d6fcd3b1SLorenzo Pieralisi hw_irq = IORT_IRQ_MASK(glb_irq[0]); 1000d6fcd3b1SLorenzo Pieralisi trigger = IORT_IRQ_TRIGGER_MASK(glb_irq[0]); 1001d6fcd3b1SLorenzo Pieralisi 1002d6fcd3b1SLorenzo Pieralisi acpi_iort_register_irq(hw_irq, "arm-smmu-global", trigger, 1003d6fcd3b1SLorenzo Pieralisi &res[num_res++]); 1004d6fcd3b1SLorenzo Pieralisi 1005d6fcd3b1SLorenzo Pieralisi /* Context IRQs */ 1006d6fcd3b1SLorenzo Pieralisi ctx_irq = ACPI_ADD_PTR(u64, node, smmu->context_interrupt_offset); 1007d6fcd3b1SLorenzo Pieralisi for (i = 0; i < smmu->context_interrupt_count; i++) { 1008d6fcd3b1SLorenzo Pieralisi hw_irq = IORT_IRQ_MASK(ctx_irq[i]); 1009d6fcd3b1SLorenzo Pieralisi trigger = IORT_IRQ_TRIGGER_MASK(ctx_irq[i]); 1010d6fcd3b1SLorenzo Pieralisi 1011d6fcd3b1SLorenzo Pieralisi acpi_iort_register_irq(hw_irq, "arm-smmu-context", trigger, 1012d6fcd3b1SLorenzo Pieralisi &res[num_res++]); 1013d6fcd3b1SLorenzo Pieralisi } 1014d6fcd3b1SLorenzo Pieralisi } 1015d6fcd3b1SLorenzo Pieralisi 1016d6fcd3b1SLorenzo Pieralisi static bool __init arm_smmu_is_coherent(struct acpi_iort_node *node) 1017d6fcd3b1SLorenzo Pieralisi { 1018d6fcd3b1SLorenzo Pieralisi struct acpi_iort_smmu *smmu; 1019d6fcd3b1SLorenzo Pieralisi 1020d6fcd3b1SLorenzo Pieralisi /* Retrieve SMMU specific data */ 1021d6fcd3b1SLorenzo Pieralisi smmu = (struct acpi_iort_smmu *)node->node_data; 1022d6fcd3b1SLorenzo Pieralisi 1023d6fcd3b1SLorenzo Pieralisi return smmu->flags & ACPI_IORT_SMMU_COHERENT_WALK; 1024d6fcd3b1SLorenzo Pieralisi } 1025d6fcd3b1SLorenzo Pieralisi 1026846f0e9eSLorenzo Pieralisi struct iort_iommu_config { 1027846f0e9eSLorenzo Pieralisi const char *name; 1028846f0e9eSLorenzo Pieralisi int (*iommu_init)(struct acpi_iort_node *node); 1029846f0e9eSLorenzo Pieralisi bool (*iommu_is_coherent)(struct acpi_iort_node *node); 1030846f0e9eSLorenzo Pieralisi int (*iommu_count_resources)(struct acpi_iort_node *node); 1031846f0e9eSLorenzo Pieralisi void (*iommu_init_resources)(struct resource *res, 1032846f0e9eSLorenzo Pieralisi struct acpi_iort_node *node); 1033846f0e9eSLorenzo Pieralisi }; 1034846f0e9eSLorenzo Pieralisi 1035e4dadfa8SLorenzo Pieralisi static const struct iort_iommu_config iort_arm_smmu_v3_cfg __initconst = { 1036e4dadfa8SLorenzo Pieralisi .name = "arm-smmu-v3", 1037e4dadfa8SLorenzo Pieralisi .iommu_is_coherent = arm_smmu_v3_is_coherent, 1038e4dadfa8SLorenzo Pieralisi .iommu_count_resources = arm_smmu_v3_count_resources, 1039e4dadfa8SLorenzo Pieralisi .iommu_init_resources = arm_smmu_v3_init_resources 1040e4dadfa8SLorenzo Pieralisi }; 1041e4dadfa8SLorenzo Pieralisi 1042d6fcd3b1SLorenzo Pieralisi static const struct iort_iommu_config iort_arm_smmu_cfg __initconst = { 1043d6fcd3b1SLorenzo Pieralisi .name = "arm-smmu", 1044d6fcd3b1SLorenzo Pieralisi .iommu_is_coherent = arm_smmu_is_coherent, 1045d6fcd3b1SLorenzo Pieralisi .iommu_count_resources = arm_smmu_count_resources, 1046d6fcd3b1SLorenzo Pieralisi .iommu_init_resources = arm_smmu_init_resources 1047d6fcd3b1SLorenzo Pieralisi }; 1048d6fcd3b1SLorenzo Pieralisi 1049846f0e9eSLorenzo Pieralisi static __init 1050846f0e9eSLorenzo Pieralisi const struct iort_iommu_config *iort_get_iommu_cfg(struct acpi_iort_node *node) 1051846f0e9eSLorenzo Pieralisi { 1052e4dadfa8SLorenzo Pieralisi switch (node->type) { 1053e4dadfa8SLorenzo Pieralisi case ACPI_IORT_NODE_SMMU_V3: 1054e4dadfa8SLorenzo Pieralisi return &iort_arm_smmu_v3_cfg; 1055d6fcd3b1SLorenzo Pieralisi case ACPI_IORT_NODE_SMMU: 1056d6fcd3b1SLorenzo Pieralisi return &iort_arm_smmu_cfg; 1057e4dadfa8SLorenzo Pieralisi default: 1058846f0e9eSLorenzo Pieralisi return NULL; 1059846f0e9eSLorenzo Pieralisi } 1060e4dadfa8SLorenzo Pieralisi } 1061846f0e9eSLorenzo Pieralisi 1062846f0e9eSLorenzo Pieralisi /** 1063846f0e9eSLorenzo Pieralisi * iort_add_smmu_platform_device() - Allocate a platform device for SMMU 1064846f0e9eSLorenzo Pieralisi * @node: Pointer to SMMU ACPI IORT node 1065846f0e9eSLorenzo Pieralisi * 1066846f0e9eSLorenzo Pieralisi * Returns: 0 on success, <0 failure 1067846f0e9eSLorenzo Pieralisi */ 1068846f0e9eSLorenzo Pieralisi static int __init iort_add_smmu_platform_device(struct acpi_iort_node *node) 1069846f0e9eSLorenzo Pieralisi { 1070846f0e9eSLorenzo Pieralisi struct fwnode_handle *fwnode; 1071846f0e9eSLorenzo Pieralisi struct platform_device *pdev; 1072846f0e9eSLorenzo Pieralisi struct resource *r; 1073846f0e9eSLorenzo Pieralisi enum dev_dma_attr attr; 1074846f0e9eSLorenzo Pieralisi int ret, count; 1075846f0e9eSLorenzo Pieralisi const struct iort_iommu_config *ops = iort_get_iommu_cfg(node); 1076846f0e9eSLorenzo Pieralisi 1077846f0e9eSLorenzo Pieralisi if (!ops) 1078846f0e9eSLorenzo Pieralisi return -ENODEV; 1079846f0e9eSLorenzo Pieralisi 1080846f0e9eSLorenzo Pieralisi pdev = platform_device_alloc(ops->name, PLATFORM_DEVID_AUTO); 1081846f0e9eSLorenzo Pieralisi if (!pdev) 10825e5afa6cSDan Carpenter return -ENOMEM; 1083846f0e9eSLorenzo Pieralisi 1084846f0e9eSLorenzo Pieralisi count = ops->iommu_count_resources(node); 1085846f0e9eSLorenzo Pieralisi 1086846f0e9eSLorenzo Pieralisi r = kcalloc(count, sizeof(*r), GFP_KERNEL); 1087846f0e9eSLorenzo Pieralisi if (!r) { 1088846f0e9eSLorenzo Pieralisi ret = -ENOMEM; 1089846f0e9eSLorenzo Pieralisi goto dev_put; 1090846f0e9eSLorenzo Pieralisi } 1091846f0e9eSLorenzo Pieralisi 1092846f0e9eSLorenzo Pieralisi ops->iommu_init_resources(r, node); 1093846f0e9eSLorenzo Pieralisi 1094846f0e9eSLorenzo Pieralisi ret = platform_device_add_resources(pdev, r, count); 1095846f0e9eSLorenzo Pieralisi /* 1096846f0e9eSLorenzo Pieralisi * Resources are duplicated in platform_device_add_resources, 1097846f0e9eSLorenzo Pieralisi * free their allocated memory 1098846f0e9eSLorenzo Pieralisi */ 1099846f0e9eSLorenzo Pieralisi kfree(r); 1100846f0e9eSLorenzo Pieralisi 1101846f0e9eSLorenzo Pieralisi if (ret) 1102846f0e9eSLorenzo Pieralisi goto dev_put; 1103846f0e9eSLorenzo Pieralisi 1104846f0e9eSLorenzo Pieralisi /* 1105846f0e9eSLorenzo Pieralisi * Add a copy of IORT node pointer to platform_data to 1106846f0e9eSLorenzo Pieralisi * be used to retrieve IORT data information. 1107846f0e9eSLorenzo Pieralisi */ 1108846f0e9eSLorenzo Pieralisi ret = platform_device_add_data(pdev, &node, sizeof(node)); 1109846f0e9eSLorenzo Pieralisi if (ret) 1110846f0e9eSLorenzo Pieralisi goto dev_put; 1111846f0e9eSLorenzo Pieralisi 1112846f0e9eSLorenzo Pieralisi /* 1113846f0e9eSLorenzo Pieralisi * We expect the dma masks to be equivalent for 1114846f0e9eSLorenzo Pieralisi * all SMMUs set-ups 1115846f0e9eSLorenzo Pieralisi */ 1116846f0e9eSLorenzo Pieralisi pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask; 1117846f0e9eSLorenzo Pieralisi 1118846f0e9eSLorenzo Pieralisi fwnode = iort_get_fwnode(node); 1119846f0e9eSLorenzo Pieralisi 1120846f0e9eSLorenzo Pieralisi if (!fwnode) { 1121846f0e9eSLorenzo Pieralisi ret = -ENODEV; 1122846f0e9eSLorenzo Pieralisi goto dev_put; 1123846f0e9eSLorenzo Pieralisi } 1124846f0e9eSLorenzo Pieralisi 1125846f0e9eSLorenzo Pieralisi pdev->dev.fwnode = fwnode; 1126846f0e9eSLorenzo Pieralisi 1127846f0e9eSLorenzo Pieralisi attr = ops->iommu_is_coherent(node) ? 1128846f0e9eSLorenzo Pieralisi DEV_DMA_COHERENT : DEV_DMA_NON_COHERENT; 1129846f0e9eSLorenzo Pieralisi 1130846f0e9eSLorenzo Pieralisi /* Configure DMA for the page table walker */ 1131846f0e9eSLorenzo Pieralisi acpi_dma_configure(&pdev->dev, attr); 1132846f0e9eSLorenzo Pieralisi 1133846f0e9eSLorenzo Pieralisi ret = platform_device_add(pdev); 1134846f0e9eSLorenzo Pieralisi if (ret) 1135846f0e9eSLorenzo Pieralisi goto dma_deconfigure; 1136846f0e9eSLorenzo Pieralisi 1137846f0e9eSLorenzo Pieralisi return 0; 1138846f0e9eSLorenzo Pieralisi 1139846f0e9eSLorenzo Pieralisi dma_deconfigure: 1140846f0e9eSLorenzo Pieralisi acpi_dma_deconfigure(&pdev->dev); 1141846f0e9eSLorenzo Pieralisi dev_put: 1142846f0e9eSLorenzo Pieralisi platform_device_put(pdev); 1143846f0e9eSLorenzo Pieralisi 1144846f0e9eSLorenzo Pieralisi return ret; 1145846f0e9eSLorenzo Pieralisi } 1146846f0e9eSLorenzo Pieralisi 1147846f0e9eSLorenzo Pieralisi static void __init iort_init_platform_devices(void) 1148846f0e9eSLorenzo Pieralisi { 1149846f0e9eSLorenzo Pieralisi struct acpi_iort_node *iort_node, *iort_end; 1150846f0e9eSLorenzo Pieralisi struct acpi_table_iort *iort; 1151846f0e9eSLorenzo Pieralisi struct fwnode_handle *fwnode; 1152846f0e9eSLorenzo Pieralisi int i, ret; 1153846f0e9eSLorenzo Pieralisi 1154846f0e9eSLorenzo Pieralisi /* 1155846f0e9eSLorenzo Pieralisi * iort_table and iort both point to the start of IORT table, but 1156846f0e9eSLorenzo Pieralisi * have different struct types 1157846f0e9eSLorenzo Pieralisi */ 1158846f0e9eSLorenzo Pieralisi iort = (struct acpi_table_iort *)iort_table; 1159846f0e9eSLorenzo Pieralisi 1160846f0e9eSLorenzo Pieralisi /* Get the first IORT node */ 1161846f0e9eSLorenzo Pieralisi iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort, 1162846f0e9eSLorenzo Pieralisi iort->node_offset); 1163846f0e9eSLorenzo Pieralisi iort_end = ACPI_ADD_PTR(struct acpi_iort_node, iort, 1164846f0e9eSLorenzo Pieralisi iort_table->length); 1165846f0e9eSLorenzo Pieralisi 1166846f0e9eSLorenzo Pieralisi for (i = 0; i < iort->node_count; i++) { 1167846f0e9eSLorenzo Pieralisi if (iort_node >= iort_end) { 1168846f0e9eSLorenzo Pieralisi pr_err("iort node pointer overflows, bad table\n"); 1169846f0e9eSLorenzo Pieralisi return; 1170846f0e9eSLorenzo Pieralisi } 1171846f0e9eSLorenzo Pieralisi 1172846f0e9eSLorenzo Pieralisi if ((iort_node->type == ACPI_IORT_NODE_SMMU) || 1173846f0e9eSLorenzo Pieralisi (iort_node->type == ACPI_IORT_NODE_SMMU_V3)) { 1174846f0e9eSLorenzo Pieralisi 1175846f0e9eSLorenzo Pieralisi fwnode = acpi_alloc_fwnode_static(); 1176846f0e9eSLorenzo Pieralisi if (!fwnode) 1177846f0e9eSLorenzo Pieralisi return; 1178846f0e9eSLorenzo Pieralisi 1179846f0e9eSLorenzo Pieralisi iort_set_fwnode(iort_node, fwnode); 1180846f0e9eSLorenzo Pieralisi 1181846f0e9eSLorenzo Pieralisi ret = iort_add_smmu_platform_device(iort_node); 1182846f0e9eSLorenzo Pieralisi if (ret) { 1183846f0e9eSLorenzo Pieralisi iort_delete_fwnode(iort_node); 1184846f0e9eSLorenzo Pieralisi acpi_free_fwnode_static(fwnode); 1185846f0e9eSLorenzo Pieralisi return; 1186846f0e9eSLorenzo Pieralisi } 1187846f0e9eSLorenzo Pieralisi } 1188846f0e9eSLorenzo Pieralisi 1189846f0e9eSLorenzo Pieralisi iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort_node, 1190846f0e9eSLorenzo Pieralisi iort_node->length); 1191846f0e9eSLorenzo Pieralisi } 1192846f0e9eSLorenzo Pieralisi } 1193846f0e9eSLorenzo Pieralisi 119488ef16d8STomasz Nowicki void __init acpi_iort_init(void) 119588ef16d8STomasz Nowicki { 119688ef16d8STomasz Nowicki acpi_status status; 119788ef16d8STomasz Nowicki 119888ef16d8STomasz Nowicki status = acpi_get_table(ACPI_SIG_IORT, 0, &iort_table); 119934ceea27SLorenzo Pieralisi if (ACPI_FAILURE(status)) { 120034ceea27SLorenzo Pieralisi if (status != AE_NOT_FOUND) { 120188ef16d8STomasz Nowicki const char *msg = acpi_format_exception(status); 120234ceea27SLorenzo Pieralisi 120388ef16d8STomasz Nowicki pr_err("Failed to get table, %s\n", msg); 120488ef16d8STomasz Nowicki } 120534ceea27SLorenzo Pieralisi 120634ceea27SLorenzo Pieralisi return; 120734ceea27SLorenzo Pieralisi } 120834ceea27SLorenzo Pieralisi 1209846f0e9eSLorenzo Pieralisi iort_init_platform_devices(); 121088ef16d8STomasz Nowicki } 1211