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> 22*846f0e9eSLorenzo Pieralisi #include <linux/iommu.h> 2388ef16d8STomasz Nowicki #include <linux/kernel.h> 247936df92SLorenzo Pieralisi #include <linux/list.h> 2588ef16d8STomasz Nowicki #include <linux/pci.h> 26*846f0e9eSLorenzo Pieralisi #include <linux/platform_device.h> 277936df92SLorenzo Pieralisi #include <linux/slab.h> 2888ef16d8STomasz Nowicki 294bf2efd2STomasz Nowicki struct iort_its_msi_chip { 304bf2efd2STomasz Nowicki struct list_head list; 314bf2efd2STomasz Nowicki struct fwnode_handle *fw_node; 324bf2efd2STomasz Nowicki u32 translation_id; 334bf2efd2STomasz Nowicki }; 344bf2efd2STomasz Nowicki 357936df92SLorenzo Pieralisi struct iort_fwnode { 367936df92SLorenzo Pieralisi struct list_head list; 377936df92SLorenzo Pieralisi struct acpi_iort_node *iort_node; 387936df92SLorenzo Pieralisi struct fwnode_handle *fwnode; 397936df92SLorenzo Pieralisi }; 407936df92SLorenzo Pieralisi static LIST_HEAD(iort_fwnode_list); 417936df92SLorenzo Pieralisi static DEFINE_SPINLOCK(iort_fwnode_lock); 427936df92SLorenzo Pieralisi 437936df92SLorenzo Pieralisi /** 447936df92SLorenzo Pieralisi * iort_set_fwnode() - Create iort_fwnode and use it to register 457936df92SLorenzo Pieralisi * iommu data in the iort_fwnode_list 467936df92SLorenzo Pieralisi * 477936df92SLorenzo Pieralisi * @node: IORT table node associated with the IOMMU 487936df92SLorenzo Pieralisi * @fwnode: fwnode associated with the IORT node 497936df92SLorenzo Pieralisi * 507936df92SLorenzo Pieralisi * Returns: 0 on success 517936df92SLorenzo Pieralisi * <0 on failure 527936df92SLorenzo Pieralisi */ 537936df92SLorenzo Pieralisi static inline int iort_set_fwnode(struct acpi_iort_node *iort_node, 547936df92SLorenzo Pieralisi struct fwnode_handle *fwnode) 557936df92SLorenzo Pieralisi { 567936df92SLorenzo Pieralisi struct iort_fwnode *np; 577936df92SLorenzo Pieralisi 587936df92SLorenzo Pieralisi np = kzalloc(sizeof(struct iort_fwnode), GFP_ATOMIC); 597936df92SLorenzo Pieralisi 607936df92SLorenzo Pieralisi if (WARN_ON(!np)) 617936df92SLorenzo Pieralisi return -ENOMEM; 627936df92SLorenzo Pieralisi 637936df92SLorenzo Pieralisi INIT_LIST_HEAD(&np->list); 647936df92SLorenzo Pieralisi np->iort_node = iort_node; 657936df92SLorenzo Pieralisi np->fwnode = fwnode; 667936df92SLorenzo Pieralisi 677936df92SLorenzo Pieralisi spin_lock(&iort_fwnode_lock); 687936df92SLorenzo Pieralisi list_add_tail(&np->list, &iort_fwnode_list); 697936df92SLorenzo Pieralisi spin_unlock(&iort_fwnode_lock); 707936df92SLorenzo Pieralisi 717936df92SLorenzo Pieralisi return 0; 727936df92SLorenzo Pieralisi } 737936df92SLorenzo Pieralisi 747936df92SLorenzo Pieralisi /** 757936df92SLorenzo Pieralisi * iort_get_fwnode() - Retrieve fwnode associated with an IORT node 767936df92SLorenzo Pieralisi * 777936df92SLorenzo Pieralisi * @node: IORT table node to be looked-up 787936df92SLorenzo Pieralisi * 797936df92SLorenzo Pieralisi * Returns: fwnode_handle pointer on success, NULL on failure 807936df92SLorenzo Pieralisi */ 817936df92SLorenzo Pieralisi static inline 827936df92SLorenzo Pieralisi struct fwnode_handle *iort_get_fwnode(struct acpi_iort_node *node) 837936df92SLorenzo Pieralisi { 847936df92SLorenzo Pieralisi struct iort_fwnode *curr; 857936df92SLorenzo Pieralisi struct fwnode_handle *fwnode = NULL; 867936df92SLorenzo Pieralisi 877936df92SLorenzo Pieralisi spin_lock(&iort_fwnode_lock); 887936df92SLorenzo Pieralisi list_for_each_entry(curr, &iort_fwnode_list, list) { 897936df92SLorenzo Pieralisi if (curr->iort_node == node) { 907936df92SLorenzo Pieralisi fwnode = curr->fwnode; 917936df92SLorenzo Pieralisi break; 927936df92SLorenzo Pieralisi } 937936df92SLorenzo Pieralisi } 947936df92SLorenzo Pieralisi spin_unlock(&iort_fwnode_lock); 957936df92SLorenzo Pieralisi 967936df92SLorenzo Pieralisi return fwnode; 977936df92SLorenzo Pieralisi } 987936df92SLorenzo Pieralisi 997936df92SLorenzo Pieralisi /** 1007936df92SLorenzo Pieralisi * iort_delete_fwnode() - Delete fwnode associated with an IORT node 1017936df92SLorenzo Pieralisi * 1027936df92SLorenzo Pieralisi * @node: IORT table node associated with fwnode to delete 1037936df92SLorenzo Pieralisi */ 1047936df92SLorenzo Pieralisi static inline void iort_delete_fwnode(struct acpi_iort_node *node) 1057936df92SLorenzo Pieralisi { 1067936df92SLorenzo Pieralisi struct iort_fwnode *curr, *tmp; 1077936df92SLorenzo Pieralisi 1087936df92SLorenzo Pieralisi spin_lock(&iort_fwnode_lock); 1097936df92SLorenzo Pieralisi list_for_each_entry_safe(curr, tmp, &iort_fwnode_list, list) { 1107936df92SLorenzo Pieralisi if (curr->iort_node == node) { 1117936df92SLorenzo Pieralisi list_del(&curr->list); 1127936df92SLorenzo Pieralisi kfree(curr); 1137936df92SLorenzo Pieralisi break; 1147936df92SLorenzo Pieralisi } 1157936df92SLorenzo Pieralisi } 1167936df92SLorenzo Pieralisi spin_unlock(&iort_fwnode_lock); 1177936df92SLorenzo Pieralisi } 1187936df92SLorenzo Pieralisi 11988ef16d8STomasz Nowicki typedef acpi_status (*iort_find_node_callback) 12088ef16d8STomasz Nowicki (struct acpi_iort_node *node, void *context); 12188ef16d8STomasz Nowicki 12288ef16d8STomasz Nowicki /* Root pointer to the mapped IORT table */ 12388ef16d8STomasz Nowicki static struct acpi_table_header *iort_table; 12488ef16d8STomasz Nowicki 12588ef16d8STomasz Nowicki static LIST_HEAD(iort_msi_chip_list); 12688ef16d8STomasz Nowicki static DEFINE_SPINLOCK(iort_msi_chip_lock); 12788ef16d8STomasz Nowicki 1284bf2efd2STomasz Nowicki /** 1294bf2efd2STomasz Nowicki * iort_register_domain_token() - register domain token and related ITS ID 1304bf2efd2STomasz Nowicki * to the list from where we can get it back later on. 1314bf2efd2STomasz Nowicki * @trans_id: ITS ID. 1324bf2efd2STomasz Nowicki * @fw_node: Domain token. 1334bf2efd2STomasz Nowicki * 1344bf2efd2STomasz Nowicki * Returns: 0 on success, -ENOMEM if no memory when allocating list element 1354bf2efd2STomasz Nowicki */ 1364bf2efd2STomasz Nowicki int iort_register_domain_token(int trans_id, struct fwnode_handle *fw_node) 1374bf2efd2STomasz Nowicki { 1384bf2efd2STomasz Nowicki struct iort_its_msi_chip *its_msi_chip; 1394bf2efd2STomasz Nowicki 1404bf2efd2STomasz Nowicki its_msi_chip = kzalloc(sizeof(*its_msi_chip), GFP_KERNEL); 1414bf2efd2STomasz Nowicki if (!its_msi_chip) 1424bf2efd2STomasz Nowicki return -ENOMEM; 1434bf2efd2STomasz Nowicki 1444bf2efd2STomasz Nowicki its_msi_chip->fw_node = fw_node; 1454bf2efd2STomasz Nowicki its_msi_chip->translation_id = trans_id; 1464bf2efd2STomasz Nowicki 1474bf2efd2STomasz Nowicki spin_lock(&iort_msi_chip_lock); 1484bf2efd2STomasz Nowicki list_add(&its_msi_chip->list, &iort_msi_chip_list); 1494bf2efd2STomasz Nowicki spin_unlock(&iort_msi_chip_lock); 1504bf2efd2STomasz Nowicki 1514bf2efd2STomasz Nowicki return 0; 1524bf2efd2STomasz Nowicki } 1534bf2efd2STomasz Nowicki 1544bf2efd2STomasz Nowicki /** 1554bf2efd2STomasz Nowicki * iort_deregister_domain_token() - Deregister domain token based on ITS ID 1564bf2efd2STomasz Nowicki * @trans_id: ITS ID. 1574bf2efd2STomasz Nowicki * 1584bf2efd2STomasz Nowicki * Returns: none. 1594bf2efd2STomasz Nowicki */ 1604bf2efd2STomasz Nowicki void iort_deregister_domain_token(int trans_id) 1614bf2efd2STomasz Nowicki { 1624bf2efd2STomasz Nowicki struct iort_its_msi_chip *its_msi_chip, *t; 1634bf2efd2STomasz Nowicki 1644bf2efd2STomasz Nowicki spin_lock(&iort_msi_chip_lock); 1654bf2efd2STomasz Nowicki list_for_each_entry_safe(its_msi_chip, t, &iort_msi_chip_list, list) { 1664bf2efd2STomasz Nowicki if (its_msi_chip->translation_id == trans_id) { 1674bf2efd2STomasz Nowicki list_del(&its_msi_chip->list); 1684bf2efd2STomasz Nowicki kfree(its_msi_chip); 1694bf2efd2STomasz Nowicki break; 1704bf2efd2STomasz Nowicki } 1714bf2efd2STomasz Nowicki } 1724bf2efd2STomasz Nowicki spin_unlock(&iort_msi_chip_lock); 1734bf2efd2STomasz Nowicki } 1744bf2efd2STomasz Nowicki 1754bf2efd2STomasz Nowicki /** 1764bf2efd2STomasz Nowicki * iort_find_domain_token() - Find domain token based on given ITS ID 1774bf2efd2STomasz Nowicki * @trans_id: ITS ID. 1784bf2efd2STomasz Nowicki * 1794bf2efd2STomasz Nowicki * Returns: domain token when find on the list, NULL otherwise 1804bf2efd2STomasz Nowicki */ 1814bf2efd2STomasz Nowicki struct fwnode_handle *iort_find_domain_token(int trans_id) 1824bf2efd2STomasz Nowicki { 1834bf2efd2STomasz Nowicki struct fwnode_handle *fw_node = NULL; 1844bf2efd2STomasz Nowicki struct iort_its_msi_chip *its_msi_chip; 1854bf2efd2STomasz Nowicki 1864bf2efd2STomasz Nowicki spin_lock(&iort_msi_chip_lock); 1874bf2efd2STomasz Nowicki list_for_each_entry(its_msi_chip, &iort_msi_chip_list, list) { 1884bf2efd2STomasz Nowicki if (its_msi_chip->translation_id == trans_id) { 1894bf2efd2STomasz Nowicki fw_node = its_msi_chip->fw_node; 1904bf2efd2STomasz Nowicki break; 1914bf2efd2STomasz Nowicki } 1924bf2efd2STomasz Nowicki } 1934bf2efd2STomasz Nowicki spin_unlock(&iort_msi_chip_lock); 1944bf2efd2STomasz Nowicki 1954bf2efd2STomasz Nowicki return fw_node; 1964bf2efd2STomasz Nowicki } 1974bf2efd2STomasz Nowicki 19888ef16d8STomasz Nowicki static struct acpi_iort_node *iort_scan_node(enum acpi_iort_node_type type, 19988ef16d8STomasz Nowicki iort_find_node_callback callback, 20088ef16d8STomasz Nowicki void *context) 20188ef16d8STomasz Nowicki { 20288ef16d8STomasz Nowicki struct acpi_iort_node *iort_node, *iort_end; 20388ef16d8STomasz Nowicki struct acpi_table_iort *iort; 20488ef16d8STomasz Nowicki int i; 20588ef16d8STomasz Nowicki 20688ef16d8STomasz Nowicki if (!iort_table) 20788ef16d8STomasz Nowicki return NULL; 20888ef16d8STomasz Nowicki 20988ef16d8STomasz Nowicki /* Get the first IORT node */ 21088ef16d8STomasz Nowicki iort = (struct acpi_table_iort *)iort_table; 21188ef16d8STomasz Nowicki iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort, 21288ef16d8STomasz Nowicki iort->node_offset); 21388ef16d8STomasz Nowicki iort_end = ACPI_ADD_PTR(struct acpi_iort_node, iort_table, 21488ef16d8STomasz Nowicki iort_table->length); 21588ef16d8STomasz Nowicki 21688ef16d8STomasz Nowicki for (i = 0; i < iort->node_count; i++) { 21788ef16d8STomasz Nowicki if (WARN_TAINT(iort_node >= iort_end, TAINT_FIRMWARE_WORKAROUND, 21888ef16d8STomasz Nowicki "IORT node pointer overflows, bad table!\n")) 21988ef16d8STomasz Nowicki return NULL; 22088ef16d8STomasz Nowicki 22188ef16d8STomasz Nowicki if (iort_node->type == type && 22288ef16d8STomasz Nowicki ACPI_SUCCESS(callback(iort_node, context))) 22388ef16d8STomasz Nowicki return iort_node; 22488ef16d8STomasz Nowicki 22588ef16d8STomasz Nowicki iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort_node, 22688ef16d8STomasz Nowicki iort_node->length); 22788ef16d8STomasz Nowicki } 22888ef16d8STomasz Nowicki 22988ef16d8STomasz Nowicki return NULL; 23088ef16d8STomasz Nowicki } 23188ef16d8STomasz Nowicki 232bdca0c07SLorenzo Pieralisi static acpi_status 233bdca0c07SLorenzo Pieralisi iort_match_type_callback(struct acpi_iort_node *node, void *context) 234bdca0c07SLorenzo Pieralisi { 235bdca0c07SLorenzo Pieralisi return AE_OK; 236bdca0c07SLorenzo Pieralisi } 237bdca0c07SLorenzo Pieralisi 238bdca0c07SLorenzo Pieralisi bool iort_node_match(u8 type) 239bdca0c07SLorenzo Pieralisi { 240bdca0c07SLorenzo Pieralisi struct acpi_iort_node *node; 241bdca0c07SLorenzo Pieralisi 242bdca0c07SLorenzo Pieralisi node = iort_scan_node(type, iort_match_type_callback, NULL); 243bdca0c07SLorenzo Pieralisi 244bdca0c07SLorenzo Pieralisi return node != NULL; 245bdca0c07SLorenzo Pieralisi } 246bdca0c07SLorenzo Pieralisi 24788ef16d8STomasz Nowicki static acpi_status iort_match_node_callback(struct acpi_iort_node *node, 24888ef16d8STomasz Nowicki void *context) 24988ef16d8STomasz Nowicki { 25088ef16d8STomasz Nowicki struct device *dev = context; 25188ef16d8STomasz Nowicki acpi_status status; 25288ef16d8STomasz Nowicki 25388ef16d8STomasz Nowicki if (node->type == ACPI_IORT_NODE_NAMED_COMPONENT) { 25488ef16d8STomasz Nowicki struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL }; 25588ef16d8STomasz Nowicki struct acpi_device *adev = to_acpi_device_node(dev->fwnode); 25688ef16d8STomasz Nowicki struct acpi_iort_named_component *ncomp; 25788ef16d8STomasz Nowicki 25888ef16d8STomasz Nowicki if (!adev) { 25988ef16d8STomasz Nowicki status = AE_NOT_FOUND; 26088ef16d8STomasz Nowicki goto out; 26188ef16d8STomasz Nowicki } 26288ef16d8STomasz Nowicki 26388ef16d8STomasz Nowicki status = acpi_get_name(adev->handle, ACPI_FULL_PATHNAME, &buf); 26488ef16d8STomasz Nowicki if (ACPI_FAILURE(status)) { 26588ef16d8STomasz Nowicki dev_warn(dev, "Can't get device full path name\n"); 26688ef16d8STomasz Nowicki goto out; 26788ef16d8STomasz Nowicki } 26888ef16d8STomasz Nowicki 26988ef16d8STomasz Nowicki ncomp = (struct acpi_iort_named_component *)node->node_data; 27088ef16d8STomasz Nowicki status = !strcmp(ncomp->device_name, buf.pointer) ? 27188ef16d8STomasz Nowicki AE_OK : AE_NOT_FOUND; 27288ef16d8STomasz Nowicki acpi_os_free(buf.pointer); 27388ef16d8STomasz Nowicki } else if (node->type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX) { 27488ef16d8STomasz Nowicki struct acpi_iort_root_complex *pci_rc; 27588ef16d8STomasz Nowicki struct pci_bus *bus; 27688ef16d8STomasz Nowicki 27788ef16d8STomasz Nowicki bus = to_pci_bus(dev); 27888ef16d8STomasz Nowicki pci_rc = (struct acpi_iort_root_complex *)node->node_data; 27988ef16d8STomasz Nowicki 28088ef16d8STomasz Nowicki /* 28188ef16d8STomasz Nowicki * It is assumed that PCI segment numbers maps one-to-one 28288ef16d8STomasz Nowicki * with root complexes. Each segment number can represent only 28388ef16d8STomasz Nowicki * one root complex. 28488ef16d8STomasz Nowicki */ 28588ef16d8STomasz Nowicki status = pci_rc->pci_segment_number == pci_domain_nr(bus) ? 28688ef16d8STomasz Nowicki AE_OK : AE_NOT_FOUND; 28788ef16d8STomasz Nowicki } else { 28888ef16d8STomasz Nowicki status = AE_NOT_FOUND; 28988ef16d8STomasz Nowicki } 29088ef16d8STomasz Nowicki out: 29188ef16d8STomasz Nowicki return status; 29288ef16d8STomasz Nowicki } 29388ef16d8STomasz Nowicki 29488ef16d8STomasz Nowicki static int iort_id_map(struct acpi_iort_id_mapping *map, u8 type, u32 rid_in, 29588ef16d8STomasz Nowicki u32 *rid_out) 29688ef16d8STomasz Nowicki { 29788ef16d8STomasz Nowicki /* Single mapping does not care for input id */ 29888ef16d8STomasz Nowicki if (map->flags & ACPI_IORT_ID_SINGLE_MAPPING) { 29988ef16d8STomasz Nowicki if (type == ACPI_IORT_NODE_NAMED_COMPONENT || 30088ef16d8STomasz Nowicki type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX) { 30188ef16d8STomasz Nowicki *rid_out = map->output_base; 30288ef16d8STomasz Nowicki return 0; 30388ef16d8STomasz Nowicki } 30488ef16d8STomasz Nowicki 30588ef16d8STomasz Nowicki pr_warn(FW_BUG "[map %p] SINGLE MAPPING flag not allowed for node type %d, skipping ID map\n", 30688ef16d8STomasz Nowicki map, type); 30788ef16d8STomasz Nowicki return -ENXIO; 30888ef16d8STomasz Nowicki } 30988ef16d8STomasz Nowicki 31088ef16d8STomasz Nowicki if (rid_in < map->input_base || 31188ef16d8STomasz Nowicki (rid_in >= map->input_base + map->id_count)) 31288ef16d8STomasz Nowicki return -ENXIO; 31388ef16d8STomasz Nowicki 31488ef16d8STomasz Nowicki *rid_out = map->output_base + (rid_in - map->input_base); 31588ef16d8STomasz Nowicki return 0; 31688ef16d8STomasz Nowicki } 31788ef16d8STomasz Nowicki 31888ef16d8STomasz Nowicki static struct acpi_iort_node *iort_node_map_rid(struct acpi_iort_node *node, 31988ef16d8STomasz Nowicki u32 rid_in, u32 *rid_out, 32088ef16d8STomasz Nowicki u8 type) 32188ef16d8STomasz Nowicki { 32288ef16d8STomasz Nowicki u32 rid = rid_in; 32388ef16d8STomasz Nowicki 32488ef16d8STomasz Nowicki /* Parse the ID mapping tree to find specified node type */ 32588ef16d8STomasz Nowicki while (node) { 32688ef16d8STomasz Nowicki struct acpi_iort_id_mapping *map; 32788ef16d8STomasz Nowicki int i; 32888ef16d8STomasz Nowicki 32988ef16d8STomasz Nowicki if (node->type == type) { 33088ef16d8STomasz Nowicki if (rid_out) 33188ef16d8STomasz Nowicki *rid_out = rid; 33288ef16d8STomasz Nowicki return node; 33388ef16d8STomasz Nowicki } 33488ef16d8STomasz Nowicki 33588ef16d8STomasz Nowicki if (!node->mapping_offset || !node->mapping_count) 33688ef16d8STomasz Nowicki goto fail_map; 33788ef16d8STomasz Nowicki 33888ef16d8STomasz Nowicki map = ACPI_ADD_PTR(struct acpi_iort_id_mapping, node, 33988ef16d8STomasz Nowicki node->mapping_offset); 34088ef16d8STomasz Nowicki 34188ef16d8STomasz Nowicki /* Firmware bug! */ 34288ef16d8STomasz Nowicki if (!map->output_reference) { 34388ef16d8STomasz Nowicki pr_err(FW_BUG "[node %p type %d] ID map has NULL parent reference\n", 34488ef16d8STomasz Nowicki node, node->type); 34588ef16d8STomasz Nowicki goto fail_map; 34688ef16d8STomasz Nowicki } 34788ef16d8STomasz Nowicki 34888ef16d8STomasz Nowicki /* Do the RID translation */ 34988ef16d8STomasz Nowicki for (i = 0; i < node->mapping_count; i++, map++) { 35088ef16d8STomasz Nowicki if (!iort_id_map(map, node->type, rid, &rid)) 35188ef16d8STomasz Nowicki break; 35288ef16d8STomasz Nowicki } 35388ef16d8STomasz Nowicki 35488ef16d8STomasz Nowicki if (i == node->mapping_count) 35588ef16d8STomasz Nowicki goto fail_map; 35688ef16d8STomasz Nowicki 35788ef16d8STomasz Nowicki node = ACPI_ADD_PTR(struct acpi_iort_node, iort_table, 35888ef16d8STomasz Nowicki map->output_reference); 35988ef16d8STomasz Nowicki } 36088ef16d8STomasz Nowicki 36188ef16d8STomasz Nowicki fail_map: 36288ef16d8STomasz Nowicki /* Map input RID to output RID unchanged on mapping failure*/ 36388ef16d8STomasz Nowicki if (rid_out) 36488ef16d8STomasz Nowicki *rid_out = rid_in; 36588ef16d8STomasz Nowicki 36688ef16d8STomasz Nowicki return NULL; 36788ef16d8STomasz Nowicki } 36888ef16d8STomasz Nowicki 36988ef16d8STomasz Nowicki static struct acpi_iort_node *iort_find_dev_node(struct device *dev) 37088ef16d8STomasz Nowicki { 37188ef16d8STomasz Nowicki struct pci_bus *pbus; 37288ef16d8STomasz Nowicki 37388ef16d8STomasz Nowicki if (!dev_is_pci(dev)) 37488ef16d8STomasz Nowicki return iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT, 37588ef16d8STomasz Nowicki iort_match_node_callback, dev); 37688ef16d8STomasz Nowicki 37788ef16d8STomasz Nowicki /* Find a PCI root bus */ 37888ef16d8STomasz Nowicki pbus = to_pci_dev(dev)->bus; 37988ef16d8STomasz Nowicki while (!pci_is_root_bus(pbus)) 38088ef16d8STomasz Nowicki pbus = pbus->parent; 38188ef16d8STomasz Nowicki 38288ef16d8STomasz Nowicki return iort_scan_node(ACPI_IORT_NODE_PCI_ROOT_COMPLEX, 38388ef16d8STomasz Nowicki iort_match_node_callback, &pbus->dev); 38488ef16d8STomasz Nowicki } 38588ef16d8STomasz Nowicki 3864bf2efd2STomasz Nowicki /** 3874bf2efd2STomasz Nowicki * iort_msi_map_rid() - Map a MSI requester ID for a device 3884bf2efd2STomasz Nowicki * @dev: The device for which the mapping is to be done. 3894bf2efd2STomasz Nowicki * @req_id: The device requester ID. 3904bf2efd2STomasz Nowicki * 3914bf2efd2STomasz Nowicki * Returns: mapped MSI RID on success, input requester ID otherwise 3924bf2efd2STomasz Nowicki */ 3934bf2efd2STomasz Nowicki u32 iort_msi_map_rid(struct device *dev, u32 req_id) 3944bf2efd2STomasz Nowicki { 3954bf2efd2STomasz Nowicki struct acpi_iort_node *node; 3964bf2efd2STomasz Nowicki u32 dev_id; 3974bf2efd2STomasz Nowicki 3984bf2efd2STomasz Nowicki node = iort_find_dev_node(dev); 3994bf2efd2STomasz Nowicki if (!node) 4004bf2efd2STomasz Nowicki return req_id; 4014bf2efd2STomasz Nowicki 4024bf2efd2STomasz Nowicki iort_node_map_rid(node, req_id, &dev_id, ACPI_IORT_NODE_ITS_GROUP); 4034bf2efd2STomasz Nowicki return dev_id; 4044bf2efd2STomasz Nowicki } 4054bf2efd2STomasz Nowicki 4064bf2efd2STomasz Nowicki /** 4074bf2efd2STomasz Nowicki * iort_dev_find_its_id() - Find the ITS identifier for a device 4084bf2efd2STomasz Nowicki * @dev: The device. 4094bf2efd2STomasz Nowicki * @idx: Index of the ITS identifier list. 4104bf2efd2STomasz Nowicki * @its_id: ITS identifier. 4114bf2efd2STomasz Nowicki * 4124bf2efd2STomasz Nowicki * Returns: 0 on success, appropriate error value otherwise 4134bf2efd2STomasz Nowicki */ 4144bf2efd2STomasz Nowicki static int iort_dev_find_its_id(struct device *dev, u32 req_id, 4154bf2efd2STomasz Nowicki unsigned int idx, int *its_id) 4164bf2efd2STomasz Nowicki { 4174bf2efd2STomasz Nowicki struct acpi_iort_its_group *its; 4184bf2efd2STomasz Nowicki struct acpi_iort_node *node; 4194bf2efd2STomasz Nowicki 4204bf2efd2STomasz Nowicki node = iort_find_dev_node(dev); 4214bf2efd2STomasz Nowicki if (!node) 4224bf2efd2STomasz Nowicki return -ENXIO; 4234bf2efd2STomasz Nowicki 4244bf2efd2STomasz Nowicki node = iort_node_map_rid(node, req_id, NULL, ACPI_IORT_NODE_ITS_GROUP); 4254bf2efd2STomasz Nowicki if (!node) 4264bf2efd2STomasz Nowicki return -ENXIO; 4274bf2efd2STomasz Nowicki 4284bf2efd2STomasz Nowicki /* Move to ITS specific data */ 4294bf2efd2STomasz Nowicki its = (struct acpi_iort_its_group *)node->node_data; 4304bf2efd2STomasz Nowicki if (idx > its->its_count) { 4314bf2efd2STomasz Nowicki dev_err(dev, "requested ITS ID index [%d] is greater than available [%d]\n", 4324bf2efd2STomasz Nowicki idx, its->its_count); 4334bf2efd2STomasz Nowicki return -ENXIO; 4344bf2efd2STomasz Nowicki } 4354bf2efd2STomasz Nowicki 4364bf2efd2STomasz Nowicki *its_id = its->identifiers[idx]; 4374bf2efd2STomasz Nowicki return 0; 4384bf2efd2STomasz Nowicki } 4394bf2efd2STomasz Nowicki 4404bf2efd2STomasz Nowicki /** 4414bf2efd2STomasz Nowicki * iort_get_device_domain() - Find MSI domain related to a device 4424bf2efd2STomasz Nowicki * @dev: The device. 4434bf2efd2STomasz Nowicki * @req_id: Requester ID for the device. 4444bf2efd2STomasz Nowicki * 4454bf2efd2STomasz Nowicki * Returns: the MSI domain for this device, NULL otherwise 4464bf2efd2STomasz Nowicki */ 4474bf2efd2STomasz Nowicki struct irq_domain *iort_get_device_domain(struct device *dev, u32 req_id) 4484bf2efd2STomasz Nowicki { 4494bf2efd2STomasz Nowicki struct fwnode_handle *handle; 4504bf2efd2STomasz Nowicki int its_id; 4514bf2efd2STomasz Nowicki 4524bf2efd2STomasz Nowicki if (iort_dev_find_its_id(dev, req_id, 0, &its_id)) 4534bf2efd2STomasz Nowicki return NULL; 4544bf2efd2STomasz Nowicki 4554bf2efd2STomasz Nowicki handle = iort_find_domain_token(its_id); 4564bf2efd2STomasz Nowicki if (!handle) 4574bf2efd2STomasz Nowicki return NULL; 4584bf2efd2STomasz Nowicki 4594bf2efd2STomasz Nowicki return irq_find_matching_fwnode(handle, DOMAIN_BUS_PCI_MSI); 4604bf2efd2STomasz Nowicki } 4614bf2efd2STomasz Nowicki 462*846f0e9eSLorenzo Pieralisi struct iort_iommu_config { 463*846f0e9eSLorenzo Pieralisi const char *name; 464*846f0e9eSLorenzo Pieralisi int (*iommu_init)(struct acpi_iort_node *node); 465*846f0e9eSLorenzo Pieralisi bool (*iommu_is_coherent)(struct acpi_iort_node *node); 466*846f0e9eSLorenzo Pieralisi int (*iommu_count_resources)(struct acpi_iort_node *node); 467*846f0e9eSLorenzo Pieralisi void (*iommu_init_resources)(struct resource *res, 468*846f0e9eSLorenzo Pieralisi struct acpi_iort_node *node); 469*846f0e9eSLorenzo Pieralisi }; 470*846f0e9eSLorenzo Pieralisi 471*846f0e9eSLorenzo Pieralisi static __init 472*846f0e9eSLorenzo Pieralisi const struct iort_iommu_config *iort_get_iommu_cfg(struct acpi_iort_node *node) 473*846f0e9eSLorenzo Pieralisi { 474*846f0e9eSLorenzo Pieralisi return NULL; 475*846f0e9eSLorenzo Pieralisi } 476*846f0e9eSLorenzo Pieralisi 477*846f0e9eSLorenzo Pieralisi /** 478*846f0e9eSLorenzo Pieralisi * iort_add_smmu_platform_device() - Allocate a platform device for SMMU 479*846f0e9eSLorenzo Pieralisi * @node: Pointer to SMMU ACPI IORT node 480*846f0e9eSLorenzo Pieralisi * 481*846f0e9eSLorenzo Pieralisi * Returns: 0 on success, <0 failure 482*846f0e9eSLorenzo Pieralisi */ 483*846f0e9eSLorenzo Pieralisi static int __init iort_add_smmu_platform_device(struct acpi_iort_node *node) 484*846f0e9eSLorenzo Pieralisi { 485*846f0e9eSLorenzo Pieralisi struct fwnode_handle *fwnode; 486*846f0e9eSLorenzo Pieralisi struct platform_device *pdev; 487*846f0e9eSLorenzo Pieralisi struct resource *r; 488*846f0e9eSLorenzo Pieralisi enum dev_dma_attr attr; 489*846f0e9eSLorenzo Pieralisi int ret, count; 490*846f0e9eSLorenzo Pieralisi const struct iort_iommu_config *ops = iort_get_iommu_cfg(node); 491*846f0e9eSLorenzo Pieralisi 492*846f0e9eSLorenzo Pieralisi if (!ops) 493*846f0e9eSLorenzo Pieralisi return -ENODEV; 494*846f0e9eSLorenzo Pieralisi 495*846f0e9eSLorenzo Pieralisi pdev = platform_device_alloc(ops->name, PLATFORM_DEVID_AUTO); 496*846f0e9eSLorenzo Pieralisi if (!pdev) 497*846f0e9eSLorenzo Pieralisi return PTR_ERR(pdev); 498*846f0e9eSLorenzo Pieralisi 499*846f0e9eSLorenzo Pieralisi count = ops->iommu_count_resources(node); 500*846f0e9eSLorenzo Pieralisi 501*846f0e9eSLorenzo Pieralisi r = kcalloc(count, sizeof(*r), GFP_KERNEL); 502*846f0e9eSLorenzo Pieralisi if (!r) { 503*846f0e9eSLorenzo Pieralisi ret = -ENOMEM; 504*846f0e9eSLorenzo Pieralisi goto dev_put; 505*846f0e9eSLorenzo Pieralisi } 506*846f0e9eSLorenzo Pieralisi 507*846f0e9eSLorenzo Pieralisi ops->iommu_init_resources(r, node); 508*846f0e9eSLorenzo Pieralisi 509*846f0e9eSLorenzo Pieralisi ret = platform_device_add_resources(pdev, r, count); 510*846f0e9eSLorenzo Pieralisi /* 511*846f0e9eSLorenzo Pieralisi * Resources are duplicated in platform_device_add_resources, 512*846f0e9eSLorenzo Pieralisi * free their allocated memory 513*846f0e9eSLorenzo Pieralisi */ 514*846f0e9eSLorenzo Pieralisi kfree(r); 515*846f0e9eSLorenzo Pieralisi 516*846f0e9eSLorenzo Pieralisi if (ret) 517*846f0e9eSLorenzo Pieralisi goto dev_put; 518*846f0e9eSLorenzo Pieralisi 519*846f0e9eSLorenzo Pieralisi /* 520*846f0e9eSLorenzo Pieralisi * Add a copy of IORT node pointer to platform_data to 521*846f0e9eSLorenzo Pieralisi * be used to retrieve IORT data information. 522*846f0e9eSLorenzo Pieralisi */ 523*846f0e9eSLorenzo Pieralisi ret = platform_device_add_data(pdev, &node, sizeof(node)); 524*846f0e9eSLorenzo Pieralisi if (ret) 525*846f0e9eSLorenzo Pieralisi goto dev_put; 526*846f0e9eSLorenzo Pieralisi 527*846f0e9eSLorenzo Pieralisi /* 528*846f0e9eSLorenzo Pieralisi * We expect the dma masks to be equivalent for 529*846f0e9eSLorenzo Pieralisi * all SMMUs set-ups 530*846f0e9eSLorenzo Pieralisi */ 531*846f0e9eSLorenzo Pieralisi pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask; 532*846f0e9eSLorenzo Pieralisi 533*846f0e9eSLorenzo Pieralisi fwnode = iort_get_fwnode(node); 534*846f0e9eSLorenzo Pieralisi 535*846f0e9eSLorenzo Pieralisi if (!fwnode) { 536*846f0e9eSLorenzo Pieralisi ret = -ENODEV; 537*846f0e9eSLorenzo Pieralisi goto dev_put; 538*846f0e9eSLorenzo Pieralisi } 539*846f0e9eSLorenzo Pieralisi 540*846f0e9eSLorenzo Pieralisi pdev->dev.fwnode = fwnode; 541*846f0e9eSLorenzo Pieralisi 542*846f0e9eSLorenzo Pieralisi attr = ops->iommu_is_coherent(node) ? 543*846f0e9eSLorenzo Pieralisi DEV_DMA_COHERENT : DEV_DMA_NON_COHERENT; 544*846f0e9eSLorenzo Pieralisi 545*846f0e9eSLorenzo Pieralisi /* Configure DMA for the page table walker */ 546*846f0e9eSLorenzo Pieralisi acpi_dma_configure(&pdev->dev, attr); 547*846f0e9eSLorenzo Pieralisi 548*846f0e9eSLorenzo Pieralisi ret = platform_device_add(pdev); 549*846f0e9eSLorenzo Pieralisi if (ret) 550*846f0e9eSLorenzo Pieralisi goto dma_deconfigure; 551*846f0e9eSLorenzo Pieralisi 552*846f0e9eSLorenzo Pieralisi return 0; 553*846f0e9eSLorenzo Pieralisi 554*846f0e9eSLorenzo Pieralisi dma_deconfigure: 555*846f0e9eSLorenzo Pieralisi acpi_dma_deconfigure(&pdev->dev); 556*846f0e9eSLorenzo Pieralisi dev_put: 557*846f0e9eSLorenzo Pieralisi platform_device_put(pdev); 558*846f0e9eSLorenzo Pieralisi 559*846f0e9eSLorenzo Pieralisi return ret; 560*846f0e9eSLorenzo Pieralisi } 561*846f0e9eSLorenzo Pieralisi 562*846f0e9eSLorenzo Pieralisi static void __init iort_init_platform_devices(void) 563*846f0e9eSLorenzo Pieralisi { 564*846f0e9eSLorenzo Pieralisi struct acpi_iort_node *iort_node, *iort_end; 565*846f0e9eSLorenzo Pieralisi struct acpi_table_iort *iort; 566*846f0e9eSLorenzo Pieralisi struct fwnode_handle *fwnode; 567*846f0e9eSLorenzo Pieralisi int i, ret; 568*846f0e9eSLorenzo Pieralisi 569*846f0e9eSLorenzo Pieralisi /* 570*846f0e9eSLorenzo Pieralisi * iort_table and iort both point to the start of IORT table, but 571*846f0e9eSLorenzo Pieralisi * have different struct types 572*846f0e9eSLorenzo Pieralisi */ 573*846f0e9eSLorenzo Pieralisi iort = (struct acpi_table_iort *)iort_table; 574*846f0e9eSLorenzo Pieralisi 575*846f0e9eSLorenzo Pieralisi /* Get the first IORT node */ 576*846f0e9eSLorenzo Pieralisi iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort, 577*846f0e9eSLorenzo Pieralisi iort->node_offset); 578*846f0e9eSLorenzo Pieralisi iort_end = ACPI_ADD_PTR(struct acpi_iort_node, iort, 579*846f0e9eSLorenzo Pieralisi iort_table->length); 580*846f0e9eSLorenzo Pieralisi 581*846f0e9eSLorenzo Pieralisi for (i = 0; i < iort->node_count; i++) { 582*846f0e9eSLorenzo Pieralisi if (iort_node >= iort_end) { 583*846f0e9eSLorenzo Pieralisi pr_err("iort node pointer overflows, bad table\n"); 584*846f0e9eSLorenzo Pieralisi return; 585*846f0e9eSLorenzo Pieralisi } 586*846f0e9eSLorenzo Pieralisi 587*846f0e9eSLorenzo Pieralisi if ((iort_node->type == ACPI_IORT_NODE_SMMU) || 588*846f0e9eSLorenzo Pieralisi (iort_node->type == ACPI_IORT_NODE_SMMU_V3)) { 589*846f0e9eSLorenzo Pieralisi 590*846f0e9eSLorenzo Pieralisi fwnode = acpi_alloc_fwnode_static(); 591*846f0e9eSLorenzo Pieralisi if (!fwnode) 592*846f0e9eSLorenzo Pieralisi return; 593*846f0e9eSLorenzo Pieralisi 594*846f0e9eSLorenzo Pieralisi iort_set_fwnode(iort_node, fwnode); 595*846f0e9eSLorenzo Pieralisi 596*846f0e9eSLorenzo Pieralisi ret = iort_add_smmu_platform_device(iort_node); 597*846f0e9eSLorenzo Pieralisi if (ret) { 598*846f0e9eSLorenzo Pieralisi iort_delete_fwnode(iort_node); 599*846f0e9eSLorenzo Pieralisi acpi_free_fwnode_static(fwnode); 600*846f0e9eSLorenzo Pieralisi return; 601*846f0e9eSLorenzo Pieralisi } 602*846f0e9eSLorenzo Pieralisi } 603*846f0e9eSLorenzo Pieralisi 604*846f0e9eSLorenzo Pieralisi iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort_node, 605*846f0e9eSLorenzo Pieralisi iort_node->length); 606*846f0e9eSLorenzo Pieralisi } 607*846f0e9eSLorenzo Pieralisi } 608*846f0e9eSLorenzo Pieralisi 60988ef16d8STomasz Nowicki void __init acpi_iort_init(void) 61088ef16d8STomasz Nowicki { 61188ef16d8STomasz Nowicki acpi_status status; 61288ef16d8STomasz Nowicki 61388ef16d8STomasz Nowicki status = acpi_get_table(ACPI_SIG_IORT, 0, &iort_table); 61434ceea27SLorenzo Pieralisi if (ACPI_FAILURE(status)) { 61534ceea27SLorenzo Pieralisi if (status != AE_NOT_FOUND) { 61688ef16d8STomasz Nowicki const char *msg = acpi_format_exception(status); 61734ceea27SLorenzo Pieralisi 61888ef16d8STomasz Nowicki pr_err("Failed to get table, %s\n", msg); 61988ef16d8STomasz Nowicki } 62034ceea27SLorenzo Pieralisi 62134ceea27SLorenzo Pieralisi return; 62234ceea27SLorenzo Pieralisi } 62334ceea27SLorenzo Pieralisi 624*846f0e9eSLorenzo Pieralisi iort_init_platform_devices(); 625*846f0e9eSLorenzo Pieralisi 62634ceea27SLorenzo Pieralisi acpi_probe_device_table(iort); 62788ef16d8STomasz Nowicki } 628