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 344bf2efd2STomasz Nowicki struct iort_its_msi_chip { 354bf2efd2STomasz Nowicki struct list_head list; 364bf2efd2STomasz Nowicki struct fwnode_handle *fw_node; 374bf2efd2STomasz Nowicki u32 translation_id; 384bf2efd2STomasz Nowicki }; 394bf2efd2STomasz Nowicki 407936df92SLorenzo Pieralisi struct iort_fwnode { 417936df92SLorenzo Pieralisi struct list_head list; 427936df92SLorenzo Pieralisi struct acpi_iort_node *iort_node; 437936df92SLorenzo Pieralisi struct fwnode_handle *fwnode; 447936df92SLorenzo Pieralisi }; 457936df92SLorenzo Pieralisi static LIST_HEAD(iort_fwnode_list); 467936df92SLorenzo Pieralisi static DEFINE_SPINLOCK(iort_fwnode_lock); 477936df92SLorenzo Pieralisi 487936df92SLorenzo Pieralisi /** 497936df92SLorenzo Pieralisi * iort_set_fwnode() - Create iort_fwnode and use it to register 507936df92SLorenzo Pieralisi * iommu data in the iort_fwnode_list 517936df92SLorenzo Pieralisi * 527936df92SLorenzo Pieralisi * @node: IORT table node associated with the IOMMU 537936df92SLorenzo Pieralisi * @fwnode: fwnode associated with the IORT node 547936df92SLorenzo Pieralisi * 557936df92SLorenzo Pieralisi * Returns: 0 on success 567936df92SLorenzo Pieralisi * <0 on failure 577936df92SLorenzo Pieralisi */ 587936df92SLorenzo Pieralisi static inline int iort_set_fwnode(struct acpi_iort_node *iort_node, 597936df92SLorenzo Pieralisi struct fwnode_handle *fwnode) 607936df92SLorenzo Pieralisi { 617936df92SLorenzo Pieralisi struct iort_fwnode *np; 627936df92SLorenzo Pieralisi 637936df92SLorenzo Pieralisi np = kzalloc(sizeof(struct iort_fwnode), GFP_ATOMIC); 647936df92SLorenzo Pieralisi 657936df92SLorenzo Pieralisi if (WARN_ON(!np)) 667936df92SLorenzo Pieralisi return -ENOMEM; 677936df92SLorenzo Pieralisi 687936df92SLorenzo Pieralisi INIT_LIST_HEAD(&np->list); 697936df92SLorenzo Pieralisi np->iort_node = iort_node; 707936df92SLorenzo Pieralisi np->fwnode = fwnode; 717936df92SLorenzo Pieralisi 727936df92SLorenzo Pieralisi spin_lock(&iort_fwnode_lock); 737936df92SLorenzo Pieralisi list_add_tail(&np->list, &iort_fwnode_list); 747936df92SLorenzo Pieralisi spin_unlock(&iort_fwnode_lock); 757936df92SLorenzo Pieralisi 767936df92SLorenzo Pieralisi return 0; 777936df92SLorenzo Pieralisi } 787936df92SLorenzo Pieralisi 797936df92SLorenzo Pieralisi /** 807936df92SLorenzo Pieralisi * iort_get_fwnode() - Retrieve fwnode associated with an IORT node 817936df92SLorenzo Pieralisi * 827936df92SLorenzo Pieralisi * @node: IORT table node to be looked-up 837936df92SLorenzo Pieralisi * 847936df92SLorenzo Pieralisi * Returns: fwnode_handle pointer on success, NULL on failure 857936df92SLorenzo Pieralisi */ 867936df92SLorenzo Pieralisi static inline 877936df92SLorenzo Pieralisi struct fwnode_handle *iort_get_fwnode(struct acpi_iort_node *node) 887936df92SLorenzo Pieralisi { 897936df92SLorenzo Pieralisi struct iort_fwnode *curr; 907936df92SLorenzo Pieralisi struct fwnode_handle *fwnode = NULL; 917936df92SLorenzo Pieralisi 927936df92SLorenzo Pieralisi spin_lock(&iort_fwnode_lock); 937936df92SLorenzo Pieralisi list_for_each_entry(curr, &iort_fwnode_list, list) { 947936df92SLorenzo Pieralisi if (curr->iort_node == node) { 957936df92SLorenzo Pieralisi fwnode = curr->fwnode; 967936df92SLorenzo Pieralisi break; 977936df92SLorenzo Pieralisi } 987936df92SLorenzo Pieralisi } 997936df92SLorenzo Pieralisi spin_unlock(&iort_fwnode_lock); 1007936df92SLorenzo Pieralisi 1017936df92SLorenzo Pieralisi return fwnode; 1027936df92SLorenzo Pieralisi } 1037936df92SLorenzo Pieralisi 1047936df92SLorenzo Pieralisi /** 1057936df92SLorenzo Pieralisi * iort_delete_fwnode() - Delete fwnode associated with an IORT node 1067936df92SLorenzo Pieralisi * 1077936df92SLorenzo Pieralisi * @node: IORT table node associated with fwnode to delete 1087936df92SLorenzo Pieralisi */ 1097936df92SLorenzo Pieralisi static inline void iort_delete_fwnode(struct acpi_iort_node *node) 1107936df92SLorenzo Pieralisi { 1117936df92SLorenzo Pieralisi struct iort_fwnode *curr, *tmp; 1127936df92SLorenzo Pieralisi 1137936df92SLorenzo Pieralisi spin_lock(&iort_fwnode_lock); 1147936df92SLorenzo Pieralisi list_for_each_entry_safe(curr, tmp, &iort_fwnode_list, list) { 1157936df92SLorenzo Pieralisi if (curr->iort_node == node) { 1167936df92SLorenzo Pieralisi list_del(&curr->list); 1177936df92SLorenzo Pieralisi kfree(curr); 1187936df92SLorenzo Pieralisi break; 1197936df92SLorenzo Pieralisi } 1207936df92SLorenzo Pieralisi } 1217936df92SLorenzo Pieralisi spin_unlock(&iort_fwnode_lock); 1227936df92SLorenzo Pieralisi } 1237936df92SLorenzo Pieralisi 12488ef16d8STomasz Nowicki typedef acpi_status (*iort_find_node_callback) 12588ef16d8STomasz Nowicki (struct acpi_iort_node *node, void *context); 12688ef16d8STomasz Nowicki 12788ef16d8STomasz Nowicki /* Root pointer to the mapped IORT table */ 12888ef16d8STomasz Nowicki static struct acpi_table_header *iort_table; 12988ef16d8STomasz Nowicki 13088ef16d8STomasz Nowicki static LIST_HEAD(iort_msi_chip_list); 13188ef16d8STomasz Nowicki static DEFINE_SPINLOCK(iort_msi_chip_lock); 13288ef16d8STomasz Nowicki 1334bf2efd2STomasz Nowicki /** 1344bf2efd2STomasz Nowicki * iort_register_domain_token() - register domain token and related ITS ID 1354bf2efd2STomasz Nowicki * to the list from where we can get it back later on. 1364bf2efd2STomasz Nowicki * @trans_id: ITS ID. 1374bf2efd2STomasz Nowicki * @fw_node: Domain token. 1384bf2efd2STomasz Nowicki * 1394bf2efd2STomasz Nowicki * Returns: 0 on success, -ENOMEM if no memory when allocating list element 1404bf2efd2STomasz Nowicki */ 1414bf2efd2STomasz Nowicki int iort_register_domain_token(int trans_id, struct fwnode_handle *fw_node) 1424bf2efd2STomasz Nowicki { 1434bf2efd2STomasz Nowicki struct iort_its_msi_chip *its_msi_chip; 1444bf2efd2STomasz Nowicki 1454bf2efd2STomasz Nowicki its_msi_chip = kzalloc(sizeof(*its_msi_chip), GFP_KERNEL); 1464bf2efd2STomasz Nowicki if (!its_msi_chip) 1474bf2efd2STomasz Nowicki return -ENOMEM; 1484bf2efd2STomasz Nowicki 1494bf2efd2STomasz Nowicki its_msi_chip->fw_node = fw_node; 1504bf2efd2STomasz Nowicki its_msi_chip->translation_id = trans_id; 1514bf2efd2STomasz Nowicki 1524bf2efd2STomasz Nowicki spin_lock(&iort_msi_chip_lock); 1534bf2efd2STomasz Nowicki list_add(&its_msi_chip->list, &iort_msi_chip_list); 1544bf2efd2STomasz Nowicki spin_unlock(&iort_msi_chip_lock); 1554bf2efd2STomasz Nowicki 1564bf2efd2STomasz Nowicki return 0; 1574bf2efd2STomasz Nowicki } 1584bf2efd2STomasz Nowicki 1594bf2efd2STomasz Nowicki /** 1604bf2efd2STomasz Nowicki * iort_deregister_domain_token() - Deregister domain token based on ITS ID 1614bf2efd2STomasz Nowicki * @trans_id: ITS ID. 1624bf2efd2STomasz Nowicki * 1634bf2efd2STomasz Nowicki * Returns: none. 1644bf2efd2STomasz Nowicki */ 1654bf2efd2STomasz Nowicki void iort_deregister_domain_token(int trans_id) 1664bf2efd2STomasz Nowicki { 1674bf2efd2STomasz Nowicki struct iort_its_msi_chip *its_msi_chip, *t; 1684bf2efd2STomasz Nowicki 1694bf2efd2STomasz Nowicki spin_lock(&iort_msi_chip_lock); 1704bf2efd2STomasz Nowicki list_for_each_entry_safe(its_msi_chip, t, &iort_msi_chip_list, list) { 1714bf2efd2STomasz Nowicki if (its_msi_chip->translation_id == trans_id) { 1724bf2efd2STomasz Nowicki list_del(&its_msi_chip->list); 1734bf2efd2STomasz Nowicki kfree(its_msi_chip); 1744bf2efd2STomasz Nowicki break; 1754bf2efd2STomasz Nowicki } 1764bf2efd2STomasz Nowicki } 1774bf2efd2STomasz Nowicki spin_unlock(&iort_msi_chip_lock); 1784bf2efd2STomasz Nowicki } 1794bf2efd2STomasz Nowicki 1804bf2efd2STomasz Nowicki /** 1814bf2efd2STomasz Nowicki * iort_find_domain_token() - Find domain token based on given ITS ID 1824bf2efd2STomasz Nowicki * @trans_id: ITS ID. 1834bf2efd2STomasz Nowicki * 1844bf2efd2STomasz Nowicki * Returns: domain token when find on the list, NULL otherwise 1854bf2efd2STomasz Nowicki */ 1864bf2efd2STomasz Nowicki struct fwnode_handle *iort_find_domain_token(int trans_id) 1874bf2efd2STomasz Nowicki { 1884bf2efd2STomasz Nowicki struct fwnode_handle *fw_node = NULL; 1894bf2efd2STomasz Nowicki struct iort_its_msi_chip *its_msi_chip; 1904bf2efd2STomasz Nowicki 1914bf2efd2STomasz Nowicki spin_lock(&iort_msi_chip_lock); 1924bf2efd2STomasz Nowicki list_for_each_entry(its_msi_chip, &iort_msi_chip_list, list) { 1934bf2efd2STomasz Nowicki if (its_msi_chip->translation_id == trans_id) { 1944bf2efd2STomasz Nowicki fw_node = its_msi_chip->fw_node; 1954bf2efd2STomasz Nowicki break; 1964bf2efd2STomasz Nowicki } 1974bf2efd2STomasz Nowicki } 1984bf2efd2STomasz Nowicki spin_unlock(&iort_msi_chip_lock); 1994bf2efd2STomasz Nowicki 2004bf2efd2STomasz Nowicki return fw_node; 2014bf2efd2STomasz Nowicki } 2024bf2efd2STomasz Nowicki 20388ef16d8STomasz Nowicki static struct acpi_iort_node *iort_scan_node(enum acpi_iort_node_type type, 20488ef16d8STomasz Nowicki iort_find_node_callback callback, 20588ef16d8STomasz Nowicki void *context) 20688ef16d8STomasz Nowicki { 20788ef16d8STomasz Nowicki struct acpi_iort_node *iort_node, *iort_end; 20888ef16d8STomasz Nowicki struct acpi_table_iort *iort; 20988ef16d8STomasz Nowicki int i; 21088ef16d8STomasz Nowicki 21188ef16d8STomasz Nowicki if (!iort_table) 21288ef16d8STomasz Nowicki return NULL; 21388ef16d8STomasz Nowicki 21488ef16d8STomasz Nowicki /* Get the first IORT node */ 21588ef16d8STomasz Nowicki iort = (struct acpi_table_iort *)iort_table; 21688ef16d8STomasz Nowicki iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort, 21788ef16d8STomasz Nowicki iort->node_offset); 21888ef16d8STomasz Nowicki iort_end = ACPI_ADD_PTR(struct acpi_iort_node, iort_table, 21988ef16d8STomasz Nowicki iort_table->length); 22088ef16d8STomasz Nowicki 22188ef16d8STomasz Nowicki for (i = 0; i < iort->node_count; i++) { 22288ef16d8STomasz Nowicki if (WARN_TAINT(iort_node >= iort_end, TAINT_FIRMWARE_WORKAROUND, 22388ef16d8STomasz Nowicki "IORT node pointer overflows, bad table!\n")) 22488ef16d8STomasz Nowicki return NULL; 22588ef16d8STomasz Nowicki 22688ef16d8STomasz Nowicki if (iort_node->type == type && 22788ef16d8STomasz Nowicki ACPI_SUCCESS(callback(iort_node, context))) 22888ef16d8STomasz Nowicki return iort_node; 22988ef16d8STomasz Nowicki 23088ef16d8STomasz Nowicki iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort_node, 23188ef16d8STomasz Nowicki iort_node->length); 23288ef16d8STomasz Nowicki } 23388ef16d8STomasz Nowicki 23488ef16d8STomasz Nowicki return NULL; 23588ef16d8STomasz Nowicki } 23688ef16d8STomasz Nowicki 237bdca0c07SLorenzo Pieralisi static acpi_status 238bdca0c07SLorenzo Pieralisi iort_match_type_callback(struct acpi_iort_node *node, void *context) 239bdca0c07SLorenzo Pieralisi { 240bdca0c07SLorenzo Pieralisi return AE_OK; 241bdca0c07SLorenzo Pieralisi } 242bdca0c07SLorenzo Pieralisi 243bdca0c07SLorenzo Pieralisi bool iort_node_match(u8 type) 244bdca0c07SLorenzo Pieralisi { 245bdca0c07SLorenzo Pieralisi struct acpi_iort_node *node; 246bdca0c07SLorenzo Pieralisi 247bdca0c07SLorenzo Pieralisi node = iort_scan_node(type, iort_match_type_callback, NULL); 248bdca0c07SLorenzo Pieralisi 249bdca0c07SLorenzo Pieralisi return node != NULL; 250bdca0c07SLorenzo Pieralisi } 251bdca0c07SLorenzo Pieralisi 25288ef16d8STomasz Nowicki static acpi_status iort_match_node_callback(struct acpi_iort_node *node, 25388ef16d8STomasz Nowicki void *context) 25488ef16d8STomasz Nowicki { 25588ef16d8STomasz Nowicki struct device *dev = context; 256c92bdfe8SHanjun Guo acpi_status status = AE_NOT_FOUND; 25788ef16d8STomasz Nowicki 25888ef16d8STomasz Nowicki if (node->type == ACPI_IORT_NODE_NAMED_COMPONENT) { 25988ef16d8STomasz Nowicki struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL }; 26088ef16d8STomasz Nowicki struct acpi_device *adev = to_acpi_device_node(dev->fwnode); 26188ef16d8STomasz Nowicki struct acpi_iort_named_component *ncomp; 26288ef16d8STomasz Nowicki 263c92bdfe8SHanjun Guo if (!adev) 26488ef16d8STomasz Nowicki goto out; 26588ef16d8STomasz Nowicki 26688ef16d8STomasz Nowicki status = acpi_get_name(adev->handle, ACPI_FULL_PATHNAME, &buf); 26788ef16d8STomasz Nowicki if (ACPI_FAILURE(status)) { 26888ef16d8STomasz Nowicki dev_warn(dev, "Can't get device full path name\n"); 26988ef16d8STomasz Nowicki goto out; 27088ef16d8STomasz Nowicki } 27188ef16d8STomasz Nowicki 27288ef16d8STomasz Nowicki ncomp = (struct acpi_iort_named_component *)node->node_data; 27388ef16d8STomasz Nowicki status = !strcmp(ncomp->device_name, buf.pointer) ? 27488ef16d8STomasz Nowicki AE_OK : AE_NOT_FOUND; 27588ef16d8STomasz Nowicki acpi_os_free(buf.pointer); 27688ef16d8STomasz Nowicki } else if (node->type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX) { 27788ef16d8STomasz Nowicki struct acpi_iort_root_complex *pci_rc; 27888ef16d8STomasz Nowicki struct pci_bus *bus; 27988ef16d8STomasz Nowicki 28088ef16d8STomasz Nowicki bus = to_pci_bus(dev); 28188ef16d8STomasz Nowicki pci_rc = (struct acpi_iort_root_complex *)node->node_data; 28288ef16d8STomasz Nowicki 28388ef16d8STomasz Nowicki /* 28488ef16d8STomasz Nowicki * It is assumed that PCI segment numbers maps one-to-one 28588ef16d8STomasz Nowicki * with root complexes. Each segment number can represent only 28688ef16d8STomasz Nowicki * one root complex. 28788ef16d8STomasz Nowicki */ 28888ef16d8STomasz Nowicki status = pci_rc->pci_segment_number == pci_domain_nr(bus) ? 28988ef16d8STomasz Nowicki AE_OK : AE_NOT_FOUND; 29088ef16d8STomasz Nowicki } 29188ef16d8STomasz Nowicki out: 29288ef16d8STomasz Nowicki return status; 29388ef16d8STomasz Nowicki } 29488ef16d8STomasz Nowicki 29588ef16d8STomasz Nowicki static int iort_id_map(struct acpi_iort_id_mapping *map, u8 type, u32 rid_in, 29688ef16d8STomasz Nowicki u32 *rid_out) 29788ef16d8STomasz Nowicki { 29888ef16d8STomasz Nowicki /* Single mapping does not care for input id */ 29988ef16d8STomasz Nowicki if (map->flags & ACPI_IORT_ID_SINGLE_MAPPING) { 30088ef16d8STomasz Nowicki if (type == ACPI_IORT_NODE_NAMED_COMPONENT || 30188ef16d8STomasz Nowicki type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX) { 30288ef16d8STomasz Nowicki *rid_out = map->output_base; 30388ef16d8STomasz Nowicki return 0; 30488ef16d8STomasz Nowicki } 30588ef16d8STomasz Nowicki 30688ef16d8STomasz Nowicki pr_warn(FW_BUG "[map %p] SINGLE MAPPING flag not allowed for node type %d, skipping ID map\n", 30788ef16d8STomasz Nowicki map, type); 30888ef16d8STomasz Nowicki return -ENXIO; 30988ef16d8STomasz Nowicki } 31088ef16d8STomasz Nowicki 31188ef16d8STomasz Nowicki if (rid_in < map->input_base || 31288ef16d8STomasz Nowicki (rid_in >= map->input_base + map->id_count)) 31388ef16d8STomasz Nowicki return -ENXIO; 31488ef16d8STomasz Nowicki 31588ef16d8STomasz Nowicki *rid_out = map->output_base + (rid_in - map->input_base); 31688ef16d8STomasz Nowicki return 0; 31788ef16d8STomasz Nowicki } 31888ef16d8STomasz Nowicki 319618f535aSLorenzo Pieralisi static 320618f535aSLorenzo Pieralisi struct acpi_iort_node *iort_node_get_id(struct acpi_iort_node *node, 321618f535aSLorenzo Pieralisi u32 *id_out, u8 type_mask, 322618f535aSLorenzo Pieralisi int index) 323618f535aSLorenzo Pieralisi { 324618f535aSLorenzo Pieralisi struct acpi_iort_node *parent; 325618f535aSLorenzo Pieralisi struct acpi_iort_id_mapping *map; 326618f535aSLorenzo Pieralisi 327618f535aSLorenzo Pieralisi if (!node->mapping_offset || !node->mapping_count || 328618f535aSLorenzo Pieralisi index >= node->mapping_count) 329618f535aSLorenzo Pieralisi return NULL; 330618f535aSLorenzo Pieralisi 331618f535aSLorenzo Pieralisi map = ACPI_ADD_PTR(struct acpi_iort_id_mapping, node, 332030abd8aSLorenzo Pieralisi node->mapping_offset + index * sizeof(*map)); 333618f535aSLorenzo Pieralisi 334618f535aSLorenzo Pieralisi /* Firmware bug! */ 335618f535aSLorenzo Pieralisi if (!map->output_reference) { 336618f535aSLorenzo Pieralisi pr_err(FW_BUG "[node %p type %d] ID map has NULL parent reference\n", 337618f535aSLorenzo Pieralisi node, node->type); 338618f535aSLorenzo Pieralisi return NULL; 339618f535aSLorenzo Pieralisi } 340618f535aSLorenzo Pieralisi 341618f535aSLorenzo Pieralisi parent = ACPI_ADD_PTR(struct acpi_iort_node, iort_table, 342618f535aSLorenzo Pieralisi map->output_reference); 343618f535aSLorenzo Pieralisi 344618f535aSLorenzo Pieralisi if (!(IORT_TYPE_MASK(parent->type) & type_mask)) 345618f535aSLorenzo Pieralisi return NULL; 346618f535aSLorenzo Pieralisi 347030abd8aSLorenzo Pieralisi if (map->flags & ACPI_IORT_ID_SINGLE_MAPPING) { 348618f535aSLorenzo Pieralisi if (node->type == ACPI_IORT_NODE_NAMED_COMPONENT || 349618f535aSLorenzo Pieralisi node->type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX) { 350030abd8aSLorenzo Pieralisi *id_out = map->output_base; 351618f535aSLorenzo Pieralisi return parent; 352618f535aSLorenzo Pieralisi } 353618f535aSLorenzo Pieralisi } 354618f535aSLorenzo Pieralisi 355618f535aSLorenzo Pieralisi return NULL; 356618f535aSLorenzo Pieralisi } 357618f535aSLorenzo Pieralisi 358*697f6093SHanjun Guo static struct acpi_iort_node *iort_node_map_id(struct acpi_iort_node *node, 359*697f6093SHanjun Guo u32 id_in, u32 *id_out, 360ea50b524SLorenzo Pieralisi u8 type_mask) 36188ef16d8STomasz Nowicki { 362*697f6093SHanjun Guo u32 id = id_in; 36388ef16d8STomasz Nowicki 36488ef16d8STomasz Nowicki /* Parse the ID mapping tree to find specified node type */ 36588ef16d8STomasz Nowicki while (node) { 36688ef16d8STomasz Nowicki struct acpi_iort_id_mapping *map; 36788ef16d8STomasz Nowicki int i; 36888ef16d8STomasz Nowicki 369ea50b524SLorenzo Pieralisi if (IORT_TYPE_MASK(node->type) & type_mask) { 370*697f6093SHanjun Guo if (id_out) 371*697f6093SHanjun Guo *id_out = id; 37288ef16d8STomasz Nowicki return node; 37388ef16d8STomasz Nowicki } 37488ef16d8STomasz Nowicki 37588ef16d8STomasz Nowicki if (!node->mapping_offset || !node->mapping_count) 37688ef16d8STomasz Nowicki goto fail_map; 37788ef16d8STomasz Nowicki 37888ef16d8STomasz Nowicki map = ACPI_ADD_PTR(struct acpi_iort_id_mapping, node, 37988ef16d8STomasz Nowicki node->mapping_offset); 38088ef16d8STomasz Nowicki 38188ef16d8STomasz Nowicki /* Firmware bug! */ 38288ef16d8STomasz Nowicki if (!map->output_reference) { 38388ef16d8STomasz Nowicki pr_err(FW_BUG "[node %p type %d] ID map has NULL parent reference\n", 38488ef16d8STomasz Nowicki node, node->type); 38588ef16d8STomasz Nowicki goto fail_map; 38688ef16d8STomasz Nowicki } 38788ef16d8STomasz Nowicki 388*697f6093SHanjun Guo /* Do the ID translation */ 38988ef16d8STomasz Nowicki for (i = 0; i < node->mapping_count; i++, map++) { 390*697f6093SHanjun Guo if (!iort_id_map(map, node->type, id, &id)) 39188ef16d8STomasz Nowicki break; 39288ef16d8STomasz Nowicki } 39388ef16d8STomasz Nowicki 39488ef16d8STomasz Nowicki if (i == node->mapping_count) 39588ef16d8STomasz Nowicki goto fail_map; 39688ef16d8STomasz Nowicki 39788ef16d8STomasz Nowicki node = ACPI_ADD_PTR(struct acpi_iort_node, iort_table, 39888ef16d8STomasz Nowicki map->output_reference); 39988ef16d8STomasz Nowicki } 40088ef16d8STomasz Nowicki 40188ef16d8STomasz Nowicki fail_map: 402*697f6093SHanjun Guo /* Map input ID to output ID unchanged on mapping failure */ 403*697f6093SHanjun Guo if (id_out) 404*697f6093SHanjun Guo *id_out = id_in; 40588ef16d8STomasz Nowicki 40688ef16d8STomasz Nowicki return NULL; 40788ef16d8STomasz Nowicki } 40888ef16d8STomasz Nowicki 40988ef16d8STomasz Nowicki static struct acpi_iort_node *iort_find_dev_node(struct device *dev) 41088ef16d8STomasz Nowicki { 41188ef16d8STomasz Nowicki struct pci_bus *pbus; 41288ef16d8STomasz Nowicki 41388ef16d8STomasz Nowicki if (!dev_is_pci(dev)) 41488ef16d8STomasz Nowicki return iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT, 41588ef16d8STomasz Nowicki iort_match_node_callback, dev); 41688ef16d8STomasz Nowicki 41788ef16d8STomasz Nowicki /* Find a PCI root bus */ 41888ef16d8STomasz Nowicki pbus = to_pci_dev(dev)->bus; 41988ef16d8STomasz Nowicki while (!pci_is_root_bus(pbus)) 42088ef16d8STomasz Nowicki pbus = pbus->parent; 42188ef16d8STomasz Nowicki 42288ef16d8STomasz Nowicki return iort_scan_node(ACPI_IORT_NODE_PCI_ROOT_COMPLEX, 42388ef16d8STomasz Nowicki iort_match_node_callback, &pbus->dev); 42488ef16d8STomasz Nowicki } 42588ef16d8STomasz Nowicki 4264bf2efd2STomasz Nowicki /** 4274bf2efd2STomasz Nowicki * iort_msi_map_rid() - Map a MSI requester ID for a device 4284bf2efd2STomasz Nowicki * @dev: The device for which the mapping is to be done. 4294bf2efd2STomasz Nowicki * @req_id: The device requester ID. 4304bf2efd2STomasz Nowicki * 4314bf2efd2STomasz Nowicki * Returns: mapped MSI RID on success, input requester ID otherwise 4324bf2efd2STomasz Nowicki */ 4334bf2efd2STomasz Nowicki u32 iort_msi_map_rid(struct device *dev, u32 req_id) 4344bf2efd2STomasz Nowicki { 4354bf2efd2STomasz Nowicki struct acpi_iort_node *node; 4364bf2efd2STomasz Nowicki u32 dev_id; 4374bf2efd2STomasz Nowicki 4384bf2efd2STomasz Nowicki node = iort_find_dev_node(dev); 4394bf2efd2STomasz Nowicki if (!node) 4404bf2efd2STomasz Nowicki return req_id; 4414bf2efd2STomasz Nowicki 442*697f6093SHanjun Guo iort_node_map_id(node, req_id, &dev_id, IORT_MSI_TYPE); 4434bf2efd2STomasz Nowicki return dev_id; 4444bf2efd2STomasz Nowicki } 4454bf2efd2STomasz Nowicki 4464bf2efd2STomasz Nowicki /** 4474bf2efd2STomasz Nowicki * iort_dev_find_its_id() - Find the ITS identifier for a device 4484bf2efd2STomasz Nowicki * @dev: The device. 4496cb6bf56SHanjun Guo * @req_id: Device's requester ID 4504bf2efd2STomasz Nowicki * @idx: Index of the ITS identifier list. 4514bf2efd2STomasz Nowicki * @its_id: ITS identifier. 4524bf2efd2STomasz Nowicki * 4534bf2efd2STomasz Nowicki * Returns: 0 on success, appropriate error value otherwise 4544bf2efd2STomasz Nowicki */ 4554bf2efd2STomasz Nowicki static int iort_dev_find_its_id(struct device *dev, u32 req_id, 4564bf2efd2STomasz Nowicki unsigned int idx, int *its_id) 4574bf2efd2STomasz Nowicki { 4584bf2efd2STomasz Nowicki struct acpi_iort_its_group *its; 4594bf2efd2STomasz Nowicki struct acpi_iort_node *node; 4604bf2efd2STomasz Nowicki 4614bf2efd2STomasz Nowicki node = iort_find_dev_node(dev); 4624bf2efd2STomasz Nowicki if (!node) 4634bf2efd2STomasz Nowicki return -ENXIO; 4644bf2efd2STomasz Nowicki 465*697f6093SHanjun Guo node = iort_node_map_id(node, req_id, NULL, IORT_MSI_TYPE); 4664bf2efd2STomasz Nowicki if (!node) 4674bf2efd2STomasz Nowicki return -ENXIO; 4684bf2efd2STomasz Nowicki 4694bf2efd2STomasz Nowicki /* Move to ITS specific data */ 4704bf2efd2STomasz Nowicki its = (struct acpi_iort_its_group *)node->node_data; 4714bf2efd2STomasz Nowicki if (idx > its->its_count) { 4724bf2efd2STomasz Nowicki dev_err(dev, "requested ITS ID index [%d] is greater than available [%d]\n", 4734bf2efd2STomasz Nowicki idx, its->its_count); 4744bf2efd2STomasz Nowicki return -ENXIO; 4754bf2efd2STomasz Nowicki } 4764bf2efd2STomasz Nowicki 4774bf2efd2STomasz Nowicki *its_id = its->identifiers[idx]; 4784bf2efd2STomasz Nowicki return 0; 4794bf2efd2STomasz Nowicki } 4804bf2efd2STomasz Nowicki 4814bf2efd2STomasz Nowicki /** 4824bf2efd2STomasz Nowicki * iort_get_device_domain() - Find MSI domain related to a device 4834bf2efd2STomasz Nowicki * @dev: The device. 4844bf2efd2STomasz Nowicki * @req_id: Requester ID for the device. 4854bf2efd2STomasz Nowicki * 4864bf2efd2STomasz Nowicki * Returns: the MSI domain for this device, NULL otherwise 4874bf2efd2STomasz Nowicki */ 4884bf2efd2STomasz Nowicki struct irq_domain *iort_get_device_domain(struct device *dev, u32 req_id) 4894bf2efd2STomasz Nowicki { 4904bf2efd2STomasz Nowicki struct fwnode_handle *handle; 4914bf2efd2STomasz Nowicki int its_id; 4924bf2efd2STomasz Nowicki 4934bf2efd2STomasz Nowicki if (iort_dev_find_its_id(dev, req_id, 0, &its_id)) 4944bf2efd2STomasz Nowicki return NULL; 4954bf2efd2STomasz Nowicki 4964bf2efd2STomasz Nowicki handle = iort_find_domain_token(its_id); 4974bf2efd2STomasz Nowicki if (!handle) 4984bf2efd2STomasz Nowicki return NULL; 4994bf2efd2STomasz Nowicki 5004bf2efd2STomasz Nowicki return irq_find_matching_fwnode(handle, DOMAIN_BUS_PCI_MSI); 5014bf2efd2STomasz Nowicki } 5024bf2efd2STomasz Nowicki 503643b8e4dSLorenzo Pieralisi static int __get_pci_rid(struct pci_dev *pdev, u16 alias, void *data) 504643b8e4dSLorenzo Pieralisi { 505643b8e4dSLorenzo Pieralisi u32 *rid = data; 506643b8e4dSLorenzo Pieralisi 507643b8e4dSLorenzo Pieralisi *rid = alias; 508643b8e4dSLorenzo Pieralisi return 0; 509643b8e4dSLorenzo Pieralisi } 510643b8e4dSLorenzo Pieralisi 511643b8e4dSLorenzo Pieralisi static int arm_smmu_iort_xlate(struct device *dev, u32 streamid, 512643b8e4dSLorenzo Pieralisi struct fwnode_handle *fwnode, 513643b8e4dSLorenzo Pieralisi const struct iommu_ops *ops) 514643b8e4dSLorenzo Pieralisi { 515643b8e4dSLorenzo Pieralisi int ret = iommu_fwspec_init(dev, fwnode, ops); 516643b8e4dSLorenzo Pieralisi 517643b8e4dSLorenzo Pieralisi if (!ret) 518643b8e4dSLorenzo Pieralisi ret = iommu_fwspec_add_ids(dev, &streamid, 1); 519643b8e4dSLorenzo Pieralisi 520643b8e4dSLorenzo Pieralisi return ret; 521643b8e4dSLorenzo Pieralisi } 522643b8e4dSLorenzo Pieralisi 523643b8e4dSLorenzo Pieralisi static const struct iommu_ops *iort_iommu_xlate(struct device *dev, 524643b8e4dSLorenzo Pieralisi struct acpi_iort_node *node, 525643b8e4dSLorenzo Pieralisi u32 streamid) 526643b8e4dSLorenzo Pieralisi { 527643b8e4dSLorenzo Pieralisi const struct iommu_ops *ops = NULL; 528643b8e4dSLorenzo Pieralisi int ret = -ENODEV; 529643b8e4dSLorenzo Pieralisi struct fwnode_handle *iort_fwnode; 530643b8e4dSLorenzo Pieralisi 531643b8e4dSLorenzo Pieralisi if (node) { 532643b8e4dSLorenzo Pieralisi iort_fwnode = iort_get_fwnode(node); 533643b8e4dSLorenzo Pieralisi if (!iort_fwnode) 534643b8e4dSLorenzo Pieralisi return NULL; 535643b8e4dSLorenzo Pieralisi 536534766dfSJoerg Roedel ops = iommu_ops_from_fwnode(iort_fwnode); 537643b8e4dSLorenzo Pieralisi if (!ops) 538643b8e4dSLorenzo Pieralisi return NULL; 539643b8e4dSLorenzo Pieralisi 540643b8e4dSLorenzo Pieralisi ret = arm_smmu_iort_xlate(dev, streamid, iort_fwnode, ops); 541643b8e4dSLorenzo Pieralisi } 542643b8e4dSLorenzo Pieralisi 543643b8e4dSLorenzo Pieralisi return ret ? NULL : ops; 544643b8e4dSLorenzo Pieralisi } 545643b8e4dSLorenzo Pieralisi 546643b8e4dSLorenzo Pieralisi /** 54718b709beSLorenzo Pieralisi * iort_set_dma_mask - Set-up dma mask for a device. 54818b709beSLorenzo Pieralisi * 54918b709beSLorenzo Pieralisi * @dev: device to configure 55018b709beSLorenzo Pieralisi */ 55118b709beSLorenzo Pieralisi void iort_set_dma_mask(struct device *dev) 55218b709beSLorenzo Pieralisi { 55318b709beSLorenzo Pieralisi /* 55418b709beSLorenzo Pieralisi * Set default coherent_dma_mask to 32 bit. Drivers are expected to 55518b709beSLorenzo Pieralisi * setup the correct supported mask. 55618b709beSLorenzo Pieralisi */ 55718b709beSLorenzo Pieralisi if (!dev->coherent_dma_mask) 55818b709beSLorenzo Pieralisi dev->coherent_dma_mask = DMA_BIT_MASK(32); 55918b709beSLorenzo Pieralisi 56018b709beSLorenzo Pieralisi /* 56118b709beSLorenzo Pieralisi * Set it to coherent_dma_mask by default if the architecture 56218b709beSLorenzo Pieralisi * code has not set it. 56318b709beSLorenzo Pieralisi */ 56418b709beSLorenzo Pieralisi if (!dev->dma_mask) 56518b709beSLorenzo Pieralisi dev->dma_mask = &dev->coherent_dma_mask; 56618b709beSLorenzo Pieralisi } 56718b709beSLorenzo Pieralisi 56818b709beSLorenzo Pieralisi /** 569643b8e4dSLorenzo Pieralisi * iort_iommu_configure - Set-up IOMMU configuration for a device. 570643b8e4dSLorenzo Pieralisi * 571643b8e4dSLorenzo Pieralisi * @dev: device to configure 572643b8e4dSLorenzo Pieralisi * 573643b8e4dSLorenzo Pieralisi * Returns: iommu_ops pointer on configuration success 574643b8e4dSLorenzo Pieralisi * NULL on configuration failure 575643b8e4dSLorenzo Pieralisi */ 576643b8e4dSLorenzo Pieralisi const struct iommu_ops *iort_iommu_configure(struct device *dev) 577643b8e4dSLorenzo Pieralisi { 578643b8e4dSLorenzo Pieralisi struct acpi_iort_node *node, *parent; 579643b8e4dSLorenzo Pieralisi const struct iommu_ops *ops = NULL; 580643b8e4dSLorenzo Pieralisi u32 streamid = 0; 581643b8e4dSLorenzo Pieralisi 582643b8e4dSLorenzo Pieralisi if (dev_is_pci(dev)) { 583643b8e4dSLorenzo Pieralisi struct pci_bus *bus = to_pci_dev(dev)->bus; 584643b8e4dSLorenzo Pieralisi u32 rid; 585643b8e4dSLorenzo Pieralisi 586643b8e4dSLorenzo Pieralisi pci_for_each_dma_alias(to_pci_dev(dev), __get_pci_rid, 587643b8e4dSLorenzo Pieralisi &rid); 588643b8e4dSLorenzo Pieralisi 589643b8e4dSLorenzo Pieralisi node = iort_scan_node(ACPI_IORT_NODE_PCI_ROOT_COMPLEX, 590643b8e4dSLorenzo Pieralisi iort_match_node_callback, &bus->dev); 591643b8e4dSLorenzo Pieralisi if (!node) 592643b8e4dSLorenzo Pieralisi return NULL; 593643b8e4dSLorenzo Pieralisi 594*697f6093SHanjun Guo parent = iort_node_map_id(node, rid, &streamid, 595643b8e4dSLorenzo Pieralisi IORT_IOMMU_TYPE); 596643b8e4dSLorenzo Pieralisi 597643b8e4dSLorenzo Pieralisi ops = iort_iommu_xlate(dev, parent, streamid); 598643b8e4dSLorenzo Pieralisi 599643b8e4dSLorenzo Pieralisi } else { 600643b8e4dSLorenzo Pieralisi int i = 0; 601643b8e4dSLorenzo Pieralisi 602643b8e4dSLorenzo Pieralisi node = iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT, 603643b8e4dSLorenzo Pieralisi iort_match_node_callback, dev); 604643b8e4dSLorenzo Pieralisi if (!node) 605643b8e4dSLorenzo Pieralisi return NULL; 606643b8e4dSLorenzo Pieralisi 607643b8e4dSLorenzo Pieralisi parent = iort_node_get_id(node, &streamid, 608643b8e4dSLorenzo Pieralisi IORT_IOMMU_TYPE, i++); 609643b8e4dSLorenzo Pieralisi 610643b8e4dSLorenzo Pieralisi while (parent) { 611643b8e4dSLorenzo Pieralisi ops = iort_iommu_xlate(dev, parent, streamid); 612643b8e4dSLorenzo Pieralisi 613643b8e4dSLorenzo Pieralisi parent = iort_node_get_id(node, &streamid, 614643b8e4dSLorenzo Pieralisi IORT_IOMMU_TYPE, i++); 615643b8e4dSLorenzo Pieralisi } 616643b8e4dSLorenzo Pieralisi } 617643b8e4dSLorenzo Pieralisi 618643b8e4dSLorenzo Pieralisi return ops; 619643b8e4dSLorenzo Pieralisi } 620643b8e4dSLorenzo Pieralisi 621e4dadfa8SLorenzo Pieralisi static void __init acpi_iort_register_irq(int hwirq, const char *name, 622e4dadfa8SLorenzo Pieralisi int trigger, 623e4dadfa8SLorenzo Pieralisi struct resource *res) 624e4dadfa8SLorenzo Pieralisi { 625e4dadfa8SLorenzo Pieralisi int irq = acpi_register_gsi(NULL, hwirq, trigger, 626e4dadfa8SLorenzo Pieralisi ACPI_ACTIVE_HIGH); 627e4dadfa8SLorenzo Pieralisi 628e4dadfa8SLorenzo Pieralisi if (irq <= 0) { 629e4dadfa8SLorenzo Pieralisi pr_err("could not register gsi hwirq %d name [%s]\n", hwirq, 630e4dadfa8SLorenzo Pieralisi name); 631e4dadfa8SLorenzo Pieralisi return; 632e4dadfa8SLorenzo Pieralisi } 633e4dadfa8SLorenzo Pieralisi 634e4dadfa8SLorenzo Pieralisi res->start = irq; 635e4dadfa8SLorenzo Pieralisi res->end = irq; 636e4dadfa8SLorenzo Pieralisi res->flags = IORESOURCE_IRQ; 637e4dadfa8SLorenzo Pieralisi res->name = name; 638e4dadfa8SLorenzo Pieralisi } 639e4dadfa8SLorenzo Pieralisi 640e4dadfa8SLorenzo Pieralisi static int __init arm_smmu_v3_count_resources(struct acpi_iort_node *node) 641e4dadfa8SLorenzo Pieralisi { 642e4dadfa8SLorenzo Pieralisi struct acpi_iort_smmu_v3 *smmu; 643e4dadfa8SLorenzo Pieralisi /* Always present mem resource */ 644e4dadfa8SLorenzo Pieralisi int num_res = 1; 645e4dadfa8SLorenzo Pieralisi 646e4dadfa8SLorenzo Pieralisi /* Retrieve SMMUv3 specific data */ 647e4dadfa8SLorenzo Pieralisi smmu = (struct acpi_iort_smmu_v3 *)node->node_data; 648e4dadfa8SLorenzo Pieralisi 649e4dadfa8SLorenzo Pieralisi if (smmu->event_gsiv) 650e4dadfa8SLorenzo Pieralisi num_res++; 651e4dadfa8SLorenzo Pieralisi 652e4dadfa8SLorenzo Pieralisi if (smmu->pri_gsiv) 653e4dadfa8SLorenzo Pieralisi num_res++; 654e4dadfa8SLorenzo Pieralisi 655e4dadfa8SLorenzo Pieralisi if (smmu->gerr_gsiv) 656e4dadfa8SLorenzo Pieralisi num_res++; 657e4dadfa8SLorenzo Pieralisi 658e4dadfa8SLorenzo Pieralisi if (smmu->sync_gsiv) 659e4dadfa8SLorenzo Pieralisi num_res++; 660e4dadfa8SLorenzo Pieralisi 661e4dadfa8SLorenzo Pieralisi return num_res; 662e4dadfa8SLorenzo Pieralisi } 663e4dadfa8SLorenzo Pieralisi 664e4dadfa8SLorenzo Pieralisi static void __init arm_smmu_v3_init_resources(struct resource *res, 665e4dadfa8SLorenzo Pieralisi struct acpi_iort_node *node) 666e4dadfa8SLorenzo Pieralisi { 667e4dadfa8SLorenzo Pieralisi struct acpi_iort_smmu_v3 *smmu; 668e4dadfa8SLorenzo Pieralisi int num_res = 0; 669e4dadfa8SLorenzo Pieralisi 670e4dadfa8SLorenzo Pieralisi /* Retrieve SMMUv3 specific data */ 671e4dadfa8SLorenzo Pieralisi smmu = (struct acpi_iort_smmu_v3 *)node->node_data; 672e4dadfa8SLorenzo Pieralisi 673e4dadfa8SLorenzo Pieralisi res[num_res].start = smmu->base_address; 674e4dadfa8SLorenzo Pieralisi res[num_res].end = smmu->base_address + SZ_128K - 1; 675e4dadfa8SLorenzo Pieralisi res[num_res].flags = IORESOURCE_MEM; 676e4dadfa8SLorenzo Pieralisi 677e4dadfa8SLorenzo Pieralisi num_res++; 678e4dadfa8SLorenzo Pieralisi 679e4dadfa8SLorenzo Pieralisi if (smmu->event_gsiv) 680e4dadfa8SLorenzo Pieralisi acpi_iort_register_irq(smmu->event_gsiv, "eventq", 681e4dadfa8SLorenzo Pieralisi ACPI_EDGE_SENSITIVE, 682e4dadfa8SLorenzo Pieralisi &res[num_res++]); 683e4dadfa8SLorenzo Pieralisi 684e4dadfa8SLorenzo Pieralisi if (smmu->pri_gsiv) 685e4dadfa8SLorenzo Pieralisi acpi_iort_register_irq(smmu->pri_gsiv, "priq", 686e4dadfa8SLorenzo Pieralisi ACPI_EDGE_SENSITIVE, 687e4dadfa8SLorenzo Pieralisi &res[num_res++]); 688e4dadfa8SLorenzo Pieralisi 689e4dadfa8SLorenzo Pieralisi if (smmu->gerr_gsiv) 690e4dadfa8SLorenzo Pieralisi acpi_iort_register_irq(smmu->gerr_gsiv, "gerror", 691e4dadfa8SLorenzo Pieralisi ACPI_EDGE_SENSITIVE, 692e4dadfa8SLorenzo Pieralisi &res[num_res++]); 693e4dadfa8SLorenzo Pieralisi 694e4dadfa8SLorenzo Pieralisi if (smmu->sync_gsiv) 695e4dadfa8SLorenzo Pieralisi acpi_iort_register_irq(smmu->sync_gsiv, "cmdq-sync", 696e4dadfa8SLorenzo Pieralisi ACPI_EDGE_SENSITIVE, 697e4dadfa8SLorenzo Pieralisi &res[num_res++]); 698e4dadfa8SLorenzo Pieralisi } 699e4dadfa8SLorenzo Pieralisi 700e4dadfa8SLorenzo Pieralisi static bool __init arm_smmu_v3_is_coherent(struct acpi_iort_node *node) 701e4dadfa8SLorenzo Pieralisi { 702e4dadfa8SLorenzo Pieralisi struct acpi_iort_smmu_v3 *smmu; 703e4dadfa8SLorenzo Pieralisi 704e4dadfa8SLorenzo Pieralisi /* Retrieve SMMUv3 specific data */ 705e4dadfa8SLorenzo Pieralisi smmu = (struct acpi_iort_smmu_v3 *)node->node_data; 706e4dadfa8SLorenzo Pieralisi 707e4dadfa8SLorenzo Pieralisi return smmu->flags & ACPI_IORT_SMMU_V3_COHACC_OVERRIDE; 708e4dadfa8SLorenzo Pieralisi } 709e4dadfa8SLorenzo Pieralisi 710d6fcd3b1SLorenzo Pieralisi static int __init arm_smmu_count_resources(struct acpi_iort_node *node) 711d6fcd3b1SLorenzo Pieralisi { 712d6fcd3b1SLorenzo Pieralisi struct acpi_iort_smmu *smmu; 713d6fcd3b1SLorenzo Pieralisi 714d6fcd3b1SLorenzo Pieralisi /* Retrieve SMMU specific data */ 715d6fcd3b1SLorenzo Pieralisi smmu = (struct acpi_iort_smmu *)node->node_data; 716d6fcd3b1SLorenzo Pieralisi 717d6fcd3b1SLorenzo Pieralisi /* 718d6fcd3b1SLorenzo Pieralisi * Only consider the global fault interrupt and ignore the 719d6fcd3b1SLorenzo Pieralisi * configuration access interrupt. 720d6fcd3b1SLorenzo Pieralisi * 721d6fcd3b1SLorenzo Pieralisi * MMIO address and global fault interrupt resources are always 722d6fcd3b1SLorenzo Pieralisi * present so add them to the context interrupt count as a static 723d6fcd3b1SLorenzo Pieralisi * value. 724d6fcd3b1SLorenzo Pieralisi */ 725d6fcd3b1SLorenzo Pieralisi return smmu->context_interrupt_count + 2; 726d6fcd3b1SLorenzo Pieralisi } 727d6fcd3b1SLorenzo Pieralisi 728d6fcd3b1SLorenzo Pieralisi static void __init arm_smmu_init_resources(struct resource *res, 729d6fcd3b1SLorenzo Pieralisi struct acpi_iort_node *node) 730d6fcd3b1SLorenzo Pieralisi { 731d6fcd3b1SLorenzo Pieralisi struct acpi_iort_smmu *smmu; 732d6fcd3b1SLorenzo Pieralisi int i, hw_irq, trigger, num_res = 0; 733d6fcd3b1SLorenzo Pieralisi u64 *ctx_irq, *glb_irq; 734d6fcd3b1SLorenzo Pieralisi 735d6fcd3b1SLorenzo Pieralisi /* Retrieve SMMU specific data */ 736d6fcd3b1SLorenzo Pieralisi smmu = (struct acpi_iort_smmu *)node->node_data; 737d6fcd3b1SLorenzo Pieralisi 738d6fcd3b1SLorenzo Pieralisi res[num_res].start = smmu->base_address; 739d6fcd3b1SLorenzo Pieralisi res[num_res].end = smmu->base_address + smmu->span - 1; 740d6fcd3b1SLorenzo Pieralisi res[num_res].flags = IORESOURCE_MEM; 741d6fcd3b1SLorenzo Pieralisi num_res++; 742d6fcd3b1SLorenzo Pieralisi 743d6fcd3b1SLorenzo Pieralisi glb_irq = ACPI_ADD_PTR(u64, node, smmu->global_interrupt_offset); 744d6fcd3b1SLorenzo Pieralisi /* Global IRQs */ 745d6fcd3b1SLorenzo Pieralisi hw_irq = IORT_IRQ_MASK(glb_irq[0]); 746d6fcd3b1SLorenzo Pieralisi trigger = IORT_IRQ_TRIGGER_MASK(glb_irq[0]); 747d6fcd3b1SLorenzo Pieralisi 748d6fcd3b1SLorenzo Pieralisi acpi_iort_register_irq(hw_irq, "arm-smmu-global", trigger, 749d6fcd3b1SLorenzo Pieralisi &res[num_res++]); 750d6fcd3b1SLorenzo Pieralisi 751d6fcd3b1SLorenzo Pieralisi /* Context IRQs */ 752d6fcd3b1SLorenzo Pieralisi ctx_irq = ACPI_ADD_PTR(u64, node, smmu->context_interrupt_offset); 753d6fcd3b1SLorenzo Pieralisi for (i = 0; i < smmu->context_interrupt_count; i++) { 754d6fcd3b1SLorenzo Pieralisi hw_irq = IORT_IRQ_MASK(ctx_irq[i]); 755d6fcd3b1SLorenzo Pieralisi trigger = IORT_IRQ_TRIGGER_MASK(ctx_irq[i]); 756d6fcd3b1SLorenzo Pieralisi 757d6fcd3b1SLorenzo Pieralisi acpi_iort_register_irq(hw_irq, "arm-smmu-context", trigger, 758d6fcd3b1SLorenzo Pieralisi &res[num_res++]); 759d6fcd3b1SLorenzo Pieralisi } 760d6fcd3b1SLorenzo Pieralisi } 761d6fcd3b1SLorenzo Pieralisi 762d6fcd3b1SLorenzo Pieralisi static bool __init arm_smmu_is_coherent(struct acpi_iort_node *node) 763d6fcd3b1SLorenzo Pieralisi { 764d6fcd3b1SLorenzo Pieralisi struct acpi_iort_smmu *smmu; 765d6fcd3b1SLorenzo Pieralisi 766d6fcd3b1SLorenzo Pieralisi /* Retrieve SMMU specific data */ 767d6fcd3b1SLorenzo Pieralisi smmu = (struct acpi_iort_smmu *)node->node_data; 768d6fcd3b1SLorenzo Pieralisi 769d6fcd3b1SLorenzo Pieralisi return smmu->flags & ACPI_IORT_SMMU_COHERENT_WALK; 770d6fcd3b1SLorenzo Pieralisi } 771d6fcd3b1SLorenzo Pieralisi 772846f0e9eSLorenzo Pieralisi struct iort_iommu_config { 773846f0e9eSLorenzo Pieralisi const char *name; 774846f0e9eSLorenzo Pieralisi int (*iommu_init)(struct acpi_iort_node *node); 775846f0e9eSLorenzo Pieralisi bool (*iommu_is_coherent)(struct acpi_iort_node *node); 776846f0e9eSLorenzo Pieralisi int (*iommu_count_resources)(struct acpi_iort_node *node); 777846f0e9eSLorenzo Pieralisi void (*iommu_init_resources)(struct resource *res, 778846f0e9eSLorenzo Pieralisi struct acpi_iort_node *node); 779846f0e9eSLorenzo Pieralisi }; 780846f0e9eSLorenzo Pieralisi 781e4dadfa8SLorenzo Pieralisi static const struct iort_iommu_config iort_arm_smmu_v3_cfg __initconst = { 782e4dadfa8SLorenzo Pieralisi .name = "arm-smmu-v3", 783e4dadfa8SLorenzo Pieralisi .iommu_is_coherent = arm_smmu_v3_is_coherent, 784e4dadfa8SLorenzo Pieralisi .iommu_count_resources = arm_smmu_v3_count_resources, 785e4dadfa8SLorenzo Pieralisi .iommu_init_resources = arm_smmu_v3_init_resources 786e4dadfa8SLorenzo Pieralisi }; 787e4dadfa8SLorenzo Pieralisi 788d6fcd3b1SLorenzo Pieralisi static const struct iort_iommu_config iort_arm_smmu_cfg __initconst = { 789d6fcd3b1SLorenzo Pieralisi .name = "arm-smmu", 790d6fcd3b1SLorenzo Pieralisi .iommu_is_coherent = arm_smmu_is_coherent, 791d6fcd3b1SLorenzo Pieralisi .iommu_count_resources = arm_smmu_count_resources, 792d6fcd3b1SLorenzo Pieralisi .iommu_init_resources = arm_smmu_init_resources 793d6fcd3b1SLorenzo Pieralisi }; 794d6fcd3b1SLorenzo Pieralisi 795846f0e9eSLorenzo Pieralisi static __init 796846f0e9eSLorenzo Pieralisi const struct iort_iommu_config *iort_get_iommu_cfg(struct acpi_iort_node *node) 797846f0e9eSLorenzo Pieralisi { 798e4dadfa8SLorenzo Pieralisi switch (node->type) { 799e4dadfa8SLorenzo Pieralisi case ACPI_IORT_NODE_SMMU_V3: 800e4dadfa8SLorenzo Pieralisi return &iort_arm_smmu_v3_cfg; 801d6fcd3b1SLorenzo Pieralisi case ACPI_IORT_NODE_SMMU: 802d6fcd3b1SLorenzo Pieralisi return &iort_arm_smmu_cfg; 803e4dadfa8SLorenzo Pieralisi default: 804846f0e9eSLorenzo Pieralisi return NULL; 805846f0e9eSLorenzo Pieralisi } 806e4dadfa8SLorenzo Pieralisi } 807846f0e9eSLorenzo Pieralisi 808846f0e9eSLorenzo Pieralisi /** 809846f0e9eSLorenzo Pieralisi * iort_add_smmu_platform_device() - Allocate a platform device for SMMU 810846f0e9eSLorenzo Pieralisi * @node: Pointer to SMMU ACPI IORT node 811846f0e9eSLorenzo Pieralisi * 812846f0e9eSLorenzo Pieralisi * Returns: 0 on success, <0 failure 813846f0e9eSLorenzo Pieralisi */ 814846f0e9eSLorenzo Pieralisi static int __init iort_add_smmu_platform_device(struct acpi_iort_node *node) 815846f0e9eSLorenzo Pieralisi { 816846f0e9eSLorenzo Pieralisi struct fwnode_handle *fwnode; 817846f0e9eSLorenzo Pieralisi struct platform_device *pdev; 818846f0e9eSLorenzo Pieralisi struct resource *r; 819846f0e9eSLorenzo Pieralisi enum dev_dma_attr attr; 820846f0e9eSLorenzo Pieralisi int ret, count; 821846f0e9eSLorenzo Pieralisi const struct iort_iommu_config *ops = iort_get_iommu_cfg(node); 822846f0e9eSLorenzo Pieralisi 823846f0e9eSLorenzo Pieralisi if (!ops) 824846f0e9eSLorenzo Pieralisi return -ENODEV; 825846f0e9eSLorenzo Pieralisi 826846f0e9eSLorenzo Pieralisi pdev = platform_device_alloc(ops->name, PLATFORM_DEVID_AUTO); 827846f0e9eSLorenzo Pieralisi if (!pdev) 8285e5afa6cSDan Carpenter return -ENOMEM; 829846f0e9eSLorenzo Pieralisi 830846f0e9eSLorenzo Pieralisi count = ops->iommu_count_resources(node); 831846f0e9eSLorenzo Pieralisi 832846f0e9eSLorenzo Pieralisi r = kcalloc(count, sizeof(*r), GFP_KERNEL); 833846f0e9eSLorenzo Pieralisi if (!r) { 834846f0e9eSLorenzo Pieralisi ret = -ENOMEM; 835846f0e9eSLorenzo Pieralisi goto dev_put; 836846f0e9eSLorenzo Pieralisi } 837846f0e9eSLorenzo Pieralisi 838846f0e9eSLorenzo Pieralisi ops->iommu_init_resources(r, node); 839846f0e9eSLorenzo Pieralisi 840846f0e9eSLorenzo Pieralisi ret = platform_device_add_resources(pdev, r, count); 841846f0e9eSLorenzo Pieralisi /* 842846f0e9eSLorenzo Pieralisi * Resources are duplicated in platform_device_add_resources, 843846f0e9eSLorenzo Pieralisi * free their allocated memory 844846f0e9eSLorenzo Pieralisi */ 845846f0e9eSLorenzo Pieralisi kfree(r); 846846f0e9eSLorenzo Pieralisi 847846f0e9eSLorenzo Pieralisi if (ret) 848846f0e9eSLorenzo Pieralisi goto dev_put; 849846f0e9eSLorenzo Pieralisi 850846f0e9eSLorenzo Pieralisi /* 851846f0e9eSLorenzo Pieralisi * Add a copy of IORT node pointer to platform_data to 852846f0e9eSLorenzo Pieralisi * be used to retrieve IORT data information. 853846f0e9eSLorenzo Pieralisi */ 854846f0e9eSLorenzo Pieralisi ret = platform_device_add_data(pdev, &node, sizeof(node)); 855846f0e9eSLorenzo Pieralisi if (ret) 856846f0e9eSLorenzo Pieralisi goto dev_put; 857846f0e9eSLorenzo Pieralisi 858846f0e9eSLorenzo Pieralisi /* 859846f0e9eSLorenzo Pieralisi * We expect the dma masks to be equivalent for 860846f0e9eSLorenzo Pieralisi * all SMMUs set-ups 861846f0e9eSLorenzo Pieralisi */ 862846f0e9eSLorenzo Pieralisi pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask; 863846f0e9eSLorenzo Pieralisi 864846f0e9eSLorenzo Pieralisi fwnode = iort_get_fwnode(node); 865846f0e9eSLorenzo Pieralisi 866846f0e9eSLorenzo Pieralisi if (!fwnode) { 867846f0e9eSLorenzo Pieralisi ret = -ENODEV; 868846f0e9eSLorenzo Pieralisi goto dev_put; 869846f0e9eSLorenzo Pieralisi } 870846f0e9eSLorenzo Pieralisi 871846f0e9eSLorenzo Pieralisi pdev->dev.fwnode = fwnode; 872846f0e9eSLorenzo Pieralisi 873846f0e9eSLorenzo Pieralisi attr = ops->iommu_is_coherent(node) ? 874846f0e9eSLorenzo Pieralisi DEV_DMA_COHERENT : DEV_DMA_NON_COHERENT; 875846f0e9eSLorenzo Pieralisi 876846f0e9eSLorenzo Pieralisi /* Configure DMA for the page table walker */ 877846f0e9eSLorenzo Pieralisi acpi_dma_configure(&pdev->dev, attr); 878846f0e9eSLorenzo Pieralisi 879846f0e9eSLorenzo Pieralisi ret = platform_device_add(pdev); 880846f0e9eSLorenzo Pieralisi if (ret) 881846f0e9eSLorenzo Pieralisi goto dma_deconfigure; 882846f0e9eSLorenzo Pieralisi 883846f0e9eSLorenzo Pieralisi return 0; 884846f0e9eSLorenzo Pieralisi 885846f0e9eSLorenzo Pieralisi dma_deconfigure: 886846f0e9eSLorenzo Pieralisi acpi_dma_deconfigure(&pdev->dev); 887846f0e9eSLorenzo Pieralisi dev_put: 888846f0e9eSLorenzo Pieralisi platform_device_put(pdev); 889846f0e9eSLorenzo Pieralisi 890846f0e9eSLorenzo Pieralisi return ret; 891846f0e9eSLorenzo Pieralisi } 892846f0e9eSLorenzo Pieralisi 893846f0e9eSLorenzo Pieralisi static void __init iort_init_platform_devices(void) 894846f0e9eSLorenzo Pieralisi { 895846f0e9eSLorenzo Pieralisi struct acpi_iort_node *iort_node, *iort_end; 896846f0e9eSLorenzo Pieralisi struct acpi_table_iort *iort; 897846f0e9eSLorenzo Pieralisi struct fwnode_handle *fwnode; 898846f0e9eSLorenzo Pieralisi int i, ret; 899846f0e9eSLorenzo Pieralisi 900846f0e9eSLorenzo Pieralisi /* 901846f0e9eSLorenzo Pieralisi * iort_table and iort both point to the start of IORT table, but 902846f0e9eSLorenzo Pieralisi * have different struct types 903846f0e9eSLorenzo Pieralisi */ 904846f0e9eSLorenzo Pieralisi iort = (struct acpi_table_iort *)iort_table; 905846f0e9eSLorenzo Pieralisi 906846f0e9eSLorenzo Pieralisi /* Get the first IORT node */ 907846f0e9eSLorenzo Pieralisi iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort, 908846f0e9eSLorenzo Pieralisi iort->node_offset); 909846f0e9eSLorenzo Pieralisi iort_end = ACPI_ADD_PTR(struct acpi_iort_node, iort, 910846f0e9eSLorenzo Pieralisi iort_table->length); 911846f0e9eSLorenzo Pieralisi 912846f0e9eSLorenzo Pieralisi for (i = 0; i < iort->node_count; i++) { 913846f0e9eSLorenzo Pieralisi if (iort_node >= iort_end) { 914846f0e9eSLorenzo Pieralisi pr_err("iort node pointer overflows, bad table\n"); 915846f0e9eSLorenzo Pieralisi return; 916846f0e9eSLorenzo Pieralisi } 917846f0e9eSLorenzo Pieralisi 918846f0e9eSLorenzo Pieralisi if ((iort_node->type == ACPI_IORT_NODE_SMMU) || 919846f0e9eSLorenzo Pieralisi (iort_node->type == ACPI_IORT_NODE_SMMU_V3)) { 920846f0e9eSLorenzo Pieralisi 921846f0e9eSLorenzo Pieralisi fwnode = acpi_alloc_fwnode_static(); 922846f0e9eSLorenzo Pieralisi if (!fwnode) 923846f0e9eSLorenzo Pieralisi return; 924846f0e9eSLorenzo Pieralisi 925846f0e9eSLorenzo Pieralisi iort_set_fwnode(iort_node, fwnode); 926846f0e9eSLorenzo Pieralisi 927846f0e9eSLorenzo Pieralisi ret = iort_add_smmu_platform_device(iort_node); 928846f0e9eSLorenzo Pieralisi if (ret) { 929846f0e9eSLorenzo Pieralisi iort_delete_fwnode(iort_node); 930846f0e9eSLorenzo Pieralisi acpi_free_fwnode_static(fwnode); 931846f0e9eSLorenzo Pieralisi return; 932846f0e9eSLorenzo Pieralisi } 933846f0e9eSLorenzo Pieralisi } 934846f0e9eSLorenzo Pieralisi 935846f0e9eSLorenzo Pieralisi iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort_node, 936846f0e9eSLorenzo Pieralisi iort_node->length); 937846f0e9eSLorenzo Pieralisi } 938846f0e9eSLorenzo Pieralisi } 939846f0e9eSLorenzo Pieralisi 94088ef16d8STomasz Nowicki void __init acpi_iort_init(void) 94188ef16d8STomasz Nowicki { 94288ef16d8STomasz Nowicki acpi_status status; 94388ef16d8STomasz Nowicki 94488ef16d8STomasz Nowicki status = acpi_get_table(ACPI_SIG_IORT, 0, &iort_table); 94534ceea27SLorenzo Pieralisi if (ACPI_FAILURE(status)) { 94634ceea27SLorenzo Pieralisi if (status != AE_NOT_FOUND) { 94788ef16d8STomasz Nowicki const char *msg = acpi_format_exception(status); 94834ceea27SLorenzo Pieralisi 94988ef16d8STomasz Nowicki pr_err("Failed to get table, %s\n", msg); 95088ef16d8STomasz Nowicki } 95134ceea27SLorenzo Pieralisi 95234ceea27SLorenzo Pieralisi return; 95334ceea27SLorenzo Pieralisi } 95434ceea27SLorenzo Pieralisi 955846f0e9eSLorenzo Pieralisi iort_init_platform_devices(); 956846f0e9eSLorenzo Pieralisi 95734ceea27SLorenzo Pieralisi acpi_probe_device_table(iort); 95888ef16d8STomasz Nowicki } 959