162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2016, Semihalf 462306a36Sopenharmony_ci * Author: Tomasz Nowicki <tn@semihalf.com> 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * This file implements early detection/parsing of I/O mapping 762306a36Sopenharmony_ci * reported to OS through firmware via I/O Remapping Table (IORT) 862306a36Sopenharmony_ci * IORT document number: ARM DEN 0049A 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#define pr_fmt(fmt) "ACPI: IORT: " fmt 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/acpi_iort.h> 1462306a36Sopenharmony_ci#include <linux/bitfield.h> 1562306a36Sopenharmony_ci#include <linux/iommu.h> 1662306a36Sopenharmony_ci#include <linux/kernel.h> 1762306a36Sopenharmony_ci#include <linux/list.h> 1862306a36Sopenharmony_ci#include <linux/pci.h> 1962306a36Sopenharmony_ci#include <linux/platform_device.h> 2062306a36Sopenharmony_ci#include <linux/slab.h> 2162306a36Sopenharmony_ci#include <linux/dma-map-ops.h> 2262306a36Sopenharmony_ci#include "init.h" 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define IORT_TYPE_MASK(type) (1 << (type)) 2562306a36Sopenharmony_ci#define IORT_MSI_TYPE (1 << ACPI_IORT_NODE_ITS_GROUP) 2662306a36Sopenharmony_ci#define IORT_IOMMU_TYPE ((1 << ACPI_IORT_NODE_SMMU) | \ 2762306a36Sopenharmony_ci (1 << ACPI_IORT_NODE_SMMU_V3)) 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistruct iort_its_msi_chip { 3062306a36Sopenharmony_ci struct list_head list; 3162306a36Sopenharmony_ci struct fwnode_handle *fw_node; 3262306a36Sopenharmony_ci phys_addr_t base_addr; 3362306a36Sopenharmony_ci u32 translation_id; 3462306a36Sopenharmony_ci}; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistruct iort_fwnode { 3762306a36Sopenharmony_ci struct list_head list; 3862306a36Sopenharmony_ci struct acpi_iort_node *iort_node; 3962306a36Sopenharmony_ci struct fwnode_handle *fwnode; 4062306a36Sopenharmony_ci}; 4162306a36Sopenharmony_cistatic LIST_HEAD(iort_fwnode_list); 4262306a36Sopenharmony_cistatic DEFINE_SPINLOCK(iort_fwnode_lock); 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci/** 4562306a36Sopenharmony_ci * iort_set_fwnode() - Create iort_fwnode and use it to register 4662306a36Sopenharmony_ci * iommu data in the iort_fwnode_list 4762306a36Sopenharmony_ci * 4862306a36Sopenharmony_ci * @iort_node: IORT table node associated with the IOMMU 4962306a36Sopenharmony_ci * @fwnode: fwnode associated with the IORT node 5062306a36Sopenharmony_ci * 5162306a36Sopenharmony_ci * Returns: 0 on success 5262306a36Sopenharmony_ci * <0 on failure 5362306a36Sopenharmony_ci */ 5462306a36Sopenharmony_cistatic inline int iort_set_fwnode(struct acpi_iort_node *iort_node, 5562306a36Sopenharmony_ci struct fwnode_handle *fwnode) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci struct iort_fwnode *np; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci np = kzalloc(sizeof(struct iort_fwnode), GFP_ATOMIC); 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci if (WARN_ON(!np)) 6262306a36Sopenharmony_ci return -ENOMEM; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci INIT_LIST_HEAD(&np->list); 6562306a36Sopenharmony_ci np->iort_node = iort_node; 6662306a36Sopenharmony_ci np->fwnode = fwnode; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci spin_lock(&iort_fwnode_lock); 6962306a36Sopenharmony_ci list_add_tail(&np->list, &iort_fwnode_list); 7062306a36Sopenharmony_ci spin_unlock(&iort_fwnode_lock); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci return 0; 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci/** 7662306a36Sopenharmony_ci * iort_get_fwnode() - Retrieve fwnode associated with an IORT node 7762306a36Sopenharmony_ci * 7862306a36Sopenharmony_ci * @node: IORT table node to be looked-up 7962306a36Sopenharmony_ci * 8062306a36Sopenharmony_ci * Returns: fwnode_handle pointer on success, NULL on failure 8162306a36Sopenharmony_ci */ 8262306a36Sopenharmony_cistatic inline struct fwnode_handle *iort_get_fwnode( 8362306a36Sopenharmony_ci struct acpi_iort_node *node) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci struct iort_fwnode *curr; 8662306a36Sopenharmony_ci struct fwnode_handle *fwnode = NULL; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci spin_lock(&iort_fwnode_lock); 8962306a36Sopenharmony_ci list_for_each_entry(curr, &iort_fwnode_list, list) { 9062306a36Sopenharmony_ci if (curr->iort_node == node) { 9162306a36Sopenharmony_ci fwnode = curr->fwnode; 9262306a36Sopenharmony_ci break; 9362306a36Sopenharmony_ci } 9462306a36Sopenharmony_ci } 9562306a36Sopenharmony_ci spin_unlock(&iort_fwnode_lock); 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci return fwnode; 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci/** 10162306a36Sopenharmony_ci * iort_delete_fwnode() - Delete fwnode associated with an IORT node 10262306a36Sopenharmony_ci * 10362306a36Sopenharmony_ci * @node: IORT table node associated with fwnode to delete 10462306a36Sopenharmony_ci */ 10562306a36Sopenharmony_cistatic inline void iort_delete_fwnode(struct acpi_iort_node *node) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci struct iort_fwnode *curr, *tmp; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci spin_lock(&iort_fwnode_lock); 11062306a36Sopenharmony_ci list_for_each_entry_safe(curr, tmp, &iort_fwnode_list, list) { 11162306a36Sopenharmony_ci if (curr->iort_node == node) { 11262306a36Sopenharmony_ci list_del(&curr->list); 11362306a36Sopenharmony_ci kfree(curr); 11462306a36Sopenharmony_ci break; 11562306a36Sopenharmony_ci } 11662306a36Sopenharmony_ci } 11762306a36Sopenharmony_ci spin_unlock(&iort_fwnode_lock); 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci/** 12162306a36Sopenharmony_ci * iort_get_iort_node() - Retrieve iort_node associated with an fwnode 12262306a36Sopenharmony_ci * 12362306a36Sopenharmony_ci * @fwnode: fwnode associated with device to be looked-up 12462306a36Sopenharmony_ci * 12562306a36Sopenharmony_ci * Returns: iort_node pointer on success, NULL on failure 12662306a36Sopenharmony_ci */ 12762306a36Sopenharmony_cistatic inline struct acpi_iort_node *iort_get_iort_node( 12862306a36Sopenharmony_ci struct fwnode_handle *fwnode) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci struct iort_fwnode *curr; 13162306a36Sopenharmony_ci struct acpi_iort_node *iort_node = NULL; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci spin_lock(&iort_fwnode_lock); 13462306a36Sopenharmony_ci list_for_each_entry(curr, &iort_fwnode_list, list) { 13562306a36Sopenharmony_ci if (curr->fwnode == fwnode) { 13662306a36Sopenharmony_ci iort_node = curr->iort_node; 13762306a36Sopenharmony_ci break; 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci spin_unlock(&iort_fwnode_lock); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci return iort_node; 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_citypedef acpi_status (*iort_find_node_callback) 14662306a36Sopenharmony_ci (struct acpi_iort_node *node, void *context); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci/* Root pointer to the mapped IORT table */ 14962306a36Sopenharmony_cistatic struct acpi_table_header *iort_table; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_cistatic LIST_HEAD(iort_msi_chip_list); 15262306a36Sopenharmony_cistatic DEFINE_SPINLOCK(iort_msi_chip_lock); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci/** 15562306a36Sopenharmony_ci * iort_register_domain_token() - register domain token along with related 15662306a36Sopenharmony_ci * ITS ID and base address to the list from where we can get it back later on. 15762306a36Sopenharmony_ci * @trans_id: ITS ID. 15862306a36Sopenharmony_ci * @base: ITS base address. 15962306a36Sopenharmony_ci * @fw_node: Domain token. 16062306a36Sopenharmony_ci * 16162306a36Sopenharmony_ci * Returns: 0 on success, -ENOMEM if no memory when allocating list element 16262306a36Sopenharmony_ci */ 16362306a36Sopenharmony_ciint iort_register_domain_token(int trans_id, phys_addr_t base, 16462306a36Sopenharmony_ci struct fwnode_handle *fw_node) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci struct iort_its_msi_chip *its_msi_chip; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci its_msi_chip = kzalloc(sizeof(*its_msi_chip), GFP_KERNEL); 16962306a36Sopenharmony_ci if (!its_msi_chip) 17062306a36Sopenharmony_ci return -ENOMEM; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci its_msi_chip->fw_node = fw_node; 17362306a36Sopenharmony_ci its_msi_chip->translation_id = trans_id; 17462306a36Sopenharmony_ci its_msi_chip->base_addr = base; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci spin_lock(&iort_msi_chip_lock); 17762306a36Sopenharmony_ci list_add(&its_msi_chip->list, &iort_msi_chip_list); 17862306a36Sopenharmony_ci spin_unlock(&iort_msi_chip_lock); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci return 0; 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci/** 18462306a36Sopenharmony_ci * iort_deregister_domain_token() - Deregister domain token based on ITS ID 18562306a36Sopenharmony_ci * @trans_id: ITS ID. 18662306a36Sopenharmony_ci * 18762306a36Sopenharmony_ci * Returns: none. 18862306a36Sopenharmony_ci */ 18962306a36Sopenharmony_civoid iort_deregister_domain_token(int trans_id) 19062306a36Sopenharmony_ci{ 19162306a36Sopenharmony_ci struct iort_its_msi_chip *its_msi_chip, *t; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci spin_lock(&iort_msi_chip_lock); 19462306a36Sopenharmony_ci list_for_each_entry_safe(its_msi_chip, t, &iort_msi_chip_list, list) { 19562306a36Sopenharmony_ci if (its_msi_chip->translation_id == trans_id) { 19662306a36Sopenharmony_ci list_del(&its_msi_chip->list); 19762306a36Sopenharmony_ci kfree(its_msi_chip); 19862306a36Sopenharmony_ci break; 19962306a36Sopenharmony_ci } 20062306a36Sopenharmony_ci } 20162306a36Sopenharmony_ci spin_unlock(&iort_msi_chip_lock); 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci/** 20562306a36Sopenharmony_ci * iort_find_domain_token() - Find domain token based on given ITS ID 20662306a36Sopenharmony_ci * @trans_id: ITS ID. 20762306a36Sopenharmony_ci * 20862306a36Sopenharmony_ci * Returns: domain token when find on the list, NULL otherwise 20962306a36Sopenharmony_ci */ 21062306a36Sopenharmony_cistruct fwnode_handle *iort_find_domain_token(int trans_id) 21162306a36Sopenharmony_ci{ 21262306a36Sopenharmony_ci struct fwnode_handle *fw_node = NULL; 21362306a36Sopenharmony_ci struct iort_its_msi_chip *its_msi_chip; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci spin_lock(&iort_msi_chip_lock); 21662306a36Sopenharmony_ci list_for_each_entry(its_msi_chip, &iort_msi_chip_list, list) { 21762306a36Sopenharmony_ci if (its_msi_chip->translation_id == trans_id) { 21862306a36Sopenharmony_ci fw_node = its_msi_chip->fw_node; 21962306a36Sopenharmony_ci break; 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci } 22262306a36Sopenharmony_ci spin_unlock(&iort_msi_chip_lock); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci return fw_node; 22562306a36Sopenharmony_ci} 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_cistatic struct acpi_iort_node *iort_scan_node(enum acpi_iort_node_type type, 22862306a36Sopenharmony_ci iort_find_node_callback callback, 22962306a36Sopenharmony_ci void *context) 23062306a36Sopenharmony_ci{ 23162306a36Sopenharmony_ci struct acpi_iort_node *iort_node, *iort_end; 23262306a36Sopenharmony_ci struct acpi_table_iort *iort; 23362306a36Sopenharmony_ci int i; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci if (!iort_table) 23662306a36Sopenharmony_ci return NULL; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci /* Get the first IORT node */ 23962306a36Sopenharmony_ci iort = (struct acpi_table_iort *)iort_table; 24062306a36Sopenharmony_ci iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort, 24162306a36Sopenharmony_ci iort->node_offset); 24262306a36Sopenharmony_ci iort_end = ACPI_ADD_PTR(struct acpi_iort_node, iort_table, 24362306a36Sopenharmony_ci iort_table->length); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci for (i = 0; i < iort->node_count; i++) { 24662306a36Sopenharmony_ci if (WARN_TAINT(iort_node >= iort_end, TAINT_FIRMWARE_WORKAROUND, 24762306a36Sopenharmony_ci "IORT node pointer overflows, bad table!\n")) 24862306a36Sopenharmony_ci return NULL; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci if (iort_node->type == type && 25162306a36Sopenharmony_ci ACPI_SUCCESS(callback(iort_node, context))) 25262306a36Sopenharmony_ci return iort_node; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort_node, 25562306a36Sopenharmony_ci iort_node->length); 25662306a36Sopenharmony_ci } 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci return NULL; 25962306a36Sopenharmony_ci} 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_cistatic acpi_status iort_match_node_callback(struct acpi_iort_node *node, 26262306a36Sopenharmony_ci void *context) 26362306a36Sopenharmony_ci{ 26462306a36Sopenharmony_ci struct device *dev = context; 26562306a36Sopenharmony_ci acpi_status status = AE_NOT_FOUND; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci if (node->type == ACPI_IORT_NODE_NAMED_COMPONENT) { 26862306a36Sopenharmony_ci struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL }; 26962306a36Sopenharmony_ci struct acpi_device *adev; 27062306a36Sopenharmony_ci struct acpi_iort_named_component *ncomp; 27162306a36Sopenharmony_ci struct device *nc_dev = dev; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci /* 27462306a36Sopenharmony_ci * Walk the device tree to find a device with an 27562306a36Sopenharmony_ci * ACPI companion; there is no point in scanning 27662306a36Sopenharmony_ci * IORT for a device matching a named component if 27762306a36Sopenharmony_ci * the device does not have an ACPI companion to 27862306a36Sopenharmony_ci * start with. 27962306a36Sopenharmony_ci */ 28062306a36Sopenharmony_ci do { 28162306a36Sopenharmony_ci adev = ACPI_COMPANION(nc_dev); 28262306a36Sopenharmony_ci if (adev) 28362306a36Sopenharmony_ci break; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci nc_dev = nc_dev->parent; 28662306a36Sopenharmony_ci } while (nc_dev); 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci if (!adev) 28962306a36Sopenharmony_ci goto out; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci status = acpi_get_name(adev->handle, ACPI_FULL_PATHNAME, &buf); 29262306a36Sopenharmony_ci if (ACPI_FAILURE(status)) { 29362306a36Sopenharmony_ci dev_warn(nc_dev, "Can't get device full path name\n"); 29462306a36Sopenharmony_ci goto out; 29562306a36Sopenharmony_ci } 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci ncomp = (struct acpi_iort_named_component *)node->node_data; 29862306a36Sopenharmony_ci status = !strcmp(ncomp->device_name, buf.pointer) ? 29962306a36Sopenharmony_ci AE_OK : AE_NOT_FOUND; 30062306a36Sopenharmony_ci acpi_os_free(buf.pointer); 30162306a36Sopenharmony_ci } else if (node->type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX) { 30262306a36Sopenharmony_ci struct acpi_iort_root_complex *pci_rc; 30362306a36Sopenharmony_ci struct pci_bus *bus; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci bus = to_pci_bus(dev); 30662306a36Sopenharmony_ci pci_rc = (struct acpi_iort_root_complex *)node->node_data; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci /* 30962306a36Sopenharmony_ci * It is assumed that PCI segment numbers maps one-to-one 31062306a36Sopenharmony_ci * with root complexes. Each segment number can represent only 31162306a36Sopenharmony_ci * one root complex. 31262306a36Sopenharmony_ci */ 31362306a36Sopenharmony_ci status = pci_rc->pci_segment_number == pci_domain_nr(bus) ? 31462306a36Sopenharmony_ci AE_OK : AE_NOT_FOUND; 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ciout: 31762306a36Sopenharmony_ci return status; 31862306a36Sopenharmony_ci} 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_cistatic int iort_id_map(struct acpi_iort_id_mapping *map, u8 type, u32 rid_in, 32162306a36Sopenharmony_ci u32 *rid_out, bool check_overlap) 32262306a36Sopenharmony_ci{ 32362306a36Sopenharmony_ci /* Single mapping does not care for input id */ 32462306a36Sopenharmony_ci if (map->flags & ACPI_IORT_ID_SINGLE_MAPPING) { 32562306a36Sopenharmony_ci if (type == ACPI_IORT_NODE_NAMED_COMPONENT || 32662306a36Sopenharmony_ci type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX) { 32762306a36Sopenharmony_ci *rid_out = map->output_base; 32862306a36Sopenharmony_ci return 0; 32962306a36Sopenharmony_ci } 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci pr_warn(FW_BUG "[map %p] SINGLE MAPPING flag not allowed for node type %d, skipping ID map\n", 33262306a36Sopenharmony_ci map, type); 33362306a36Sopenharmony_ci return -ENXIO; 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci if (rid_in < map->input_base || 33762306a36Sopenharmony_ci (rid_in > map->input_base + map->id_count)) 33862306a36Sopenharmony_ci return -ENXIO; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci if (check_overlap) { 34162306a36Sopenharmony_ci /* 34262306a36Sopenharmony_ci * We already found a mapping for this input ID at the end of 34362306a36Sopenharmony_ci * another region. If it coincides with the start of this 34462306a36Sopenharmony_ci * region, we assume the prior match was due to the off-by-1 34562306a36Sopenharmony_ci * issue mentioned below, and allow it to be superseded. 34662306a36Sopenharmony_ci * Otherwise, things are *really* broken, and we just disregard 34762306a36Sopenharmony_ci * duplicate matches entirely to retain compatibility. 34862306a36Sopenharmony_ci */ 34962306a36Sopenharmony_ci pr_err(FW_BUG "[map %p] conflicting mapping for input ID 0x%x\n", 35062306a36Sopenharmony_ci map, rid_in); 35162306a36Sopenharmony_ci if (rid_in != map->input_base) 35262306a36Sopenharmony_ci return -ENXIO; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci pr_err(FW_BUG "applying workaround.\n"); 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci *rid_out = map->output_base + (rid_in - map->input_base); 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci /* 36062306a36Sopenharmony_ci * Due to confusion regarding the meaning of the id_count field (which 36162306a36Sopenharmony_ci * carries the number of IDs *minus 1*), we may have to disregard this 36262306a36Sopenharmony_ci * match if it is at the end of the range, and overlaps with the start 36362306a36Sopenharmony_ci * of another one. 36462306a36Sopenharmony_ci */ 36562306a36Sopenharmony_ci if (map->id_count > 0 && rid_in == map->input_base + map->id_count) 36662306a36Sopenharmony_ci return -EAGAIN; 36762306a36Sopenharmony_ci return 0; 36862306a36Sopenharmony_ci} 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_cistatic struct acpi_iort_node *iort_node_get_id(struct acpi_iort_node *node, 37162306a36Sopenharmony_ci u32 *id_out, int index) 37262306a36Sopenharmony_ci{ 37362306a36Sopenharmony_ci struct acpi_iort_node *parent; 37462306a36Sopenharmony_ci struct acpi_iort_id_mapping *map; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci if (!node->mapping_offset || !node->mapping_count || 37762306a36Sopenharmony_ci index >= node->mapping_count) 37862306a36Sopenharmony_ci return NULL; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci map = ACPI_ADD_PTR(struct acpi_iort_id_mapping, node, 38162306a36Sopenharmony_ci node->mapping_offset + index * sizeof(*map)); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci /* Firmware bug! */ 38462306a36Sopenharmony_ci if (!map->output_reference) { 38562306a36Sopenharmony_ci pr_err(FW_BUG "[node %p type %d] ID map has NULL parent reference\n", 38662306a36Sopenharmony_ci node, node->type); 38762306a36Sopenharmony_ci return NULL; 38862306a36Sopenharmony_ci } 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci parent = ACPI_ADD_PTR(struct acpi_iort_node, iort_table, 39162306a36Sopenharmony_ci map->output_reference); 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci if (map->flags & ACPI_IORT_ID_SINGLE_MAPPING) { 39462306a36Sopenharmony_ci if (node->type == ACPI_IORT_NODE_NAMED_COMPONENT || 39562306a36Sopenharmony_ci node->type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX || 39662306a36Sopenharmony_ci node->type == ACPI_IORT_NODE_SMMU_V3 || 39762306a36Sopenharmony_ci node->type == ACPI_IORT_NODE_PMCG) { 39862306a36Sopenharmony_ci *id_out = map->output_base; 39962306a36Sopenharmony_ci return parent; 40062306a36Sopenharmony_ci } 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci return NULL; 40462306a36Sopenharmony_ci} 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci#ifndef ACPI_IORT_SMMU_V3_DEVICEID_VALID 40762306a36Sopenharmony_ci#define ACPI_IORT_SMMU_V3_DEVICEID_VALID (1 << 4) 40862306a36Sopenharmony_ci#endif 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_cistatic int iort_get_id_mapping_index(struct acpi_iort_node *node) 41162306a36Sopenharmony_ci{ 41262306a36Sopenharmony_ci struct acpi_iort_smmu_v3 *smmu; 41362306a36Sopenharmony_ci struct acpi_iort_pmcg *pmcg; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci switch (node->type) { 41662306a36Sopenharmony_ci case ACPI_IORT_NODE_SMMU_V3: 41762306a36Sopenharmony_ci /* 41862306a36Sopenharmony_ci * SMMUv3 dev ID mapping index was introduced in revision 1 41962306a36Sopenharmony_ci * table, not available in revision 0 42062306a36Sopenharmony_ci */ 42162306a36Sopenharmony_ci if (node->revision < 1) 42262306a36Sopenharmony_ci return -EINVAL; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci smmu = (struct acpi_iort_smmu_v3 *)node->node_data; 42562306a36Sopenharmony_ci /* 42662306a36Sopenharmony_ci * Until IORT E.e (node rev. 5), the ID mapping index was 42762306a36Sopenharmony_ci * defined to be valid unless all interrupts are GSIV-based. 42862306a36Sopenharmony_ci */ 42962306a36Sopenharmony_ci if (node->revision < 5) { 43062306a36Sopenharmony_ci if (smmu->event_gsiv && smmu->pri_gsiv && 43162306a36Sopenharmony_ci smmu->gerr_gsiv && smmu->sync_gsiv) 43262306a36Sopenharmony_ci return -EINVAL; 43362306a36Sopenharmony_ci } else if (!(smmu->flags & ACPI_IORT_SMMU_V3_DEVICEID_VALID)) { 43462306a36Sopenharmony_ci return -EINVAL; 43562306a36Sopenharmony_ci } 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci if (smmu->id_mapping_index >= node->mapping_count) { 43862306a36Sopenharmony_ci pr_err(FW_BUG "[node %p type %d] ID mapping index overflows valid mappings\n", 43962306a36Sopenharmony_ci node, node->type); 44062306a36Sopenharmony_ci return -EINVAL; 44162306a36Sopenharmony_ci } 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci return smmu->id_mapping_index; 44462306a36Sopenharmony_ci case ACPI_IORT_NODE_PMCG: 44562306a36Sopenharmony_ci pmcg = (struct acpi_iort_pmcg *)node->node_data; 44662306a36Sopenharmony_ci if (pmcg->overflow_gsiv || node->mapping_count == 0) 44762306a36Sopenharmony_ci return -EINVAL; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci return 0; 45062306a36Sopenharmony_ci default: 45162306a36Sopenharmony_ci return -EINVAL; 45262306a36Sopenharmony_ci } 45362306a36Sopenharmony_ci} 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_cistatic struct acpi_iort_node *iort_node_map_id(struct acpi_iort_node *node, 45662306a36Sopenharmony_ci u32 id_in, u32 *id_out, 45762306a36Sopenharmony_ci u8 type_mask) 45862306a36Sopenharmony_ci{ 45962306a36Sopenharmony_ci u32 id = id_in; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci /* Parse the ID mapping tree to find specified node type */ 46262306a36Sopenharmony_ci while (node) { 46362306a36Sopenharmony_ci struct acpi_iort_id_mapping *map; 46462306a36Sopenharmony_ci int i, index, rc = 0; 46562306a36Sopenharmony_ci u32 out_ref = 0, map_id = id; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci if (IORT_TYPE_MASK(node->type) & type_mask) { 46862306a36Sopenharmony_ci if (id_out) 46962306a36Sopenharmony_ci *id_out = id; 47062306a36Sopenharmony_ci return node; 47162306a36Sopenharmony_ci } 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci if (!node->mapping_offset || !node->mapping_count) 47462306a36Sopenharmony_ci goto fail_map; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci map = ACPI_ADD_PTR(struct acpi_iort_id_mapping, node, 47762306a36Sopenharmony_ci node->mapping_offset); 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci /* Firmware bug! */ 48062306a36Sopenharmony_ci if (!map->output_reference) { 48162306a36Sopenharmony_ci pr_err(FW_BUG "[node %p type %d] ID map has NULL parent reference\n", 48262306a36Sopenharmony_ci node, node->type); 48362306a36Sopenharmony_ci goto fail_map; 48462306a36Sopenharmony_ci } 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci /* 48762306a36Sopenharmony_ci * Get the special ID mapping index (if any) and skip its 48862306a36Sopenharmony_ci * associated ID map to prevent erroneous multi-stage 48962306a36Sopenharmony_ci * IORT ID translations. 49062306a36Sopenharmony_ci */ 49162306a36Sopenharmony_ci index = iort_get_id_mapping_index(node); 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci /* Do the ID translation */ 49462306a36Sopenharmony_ci for (i = 0; i < node->mapping_count; i++, map++) { 49562306a36Sopenharmony_ci /* if it is special mapping index, skip it */ 49662306a36Sopenharmony_ci if (i == index) 49762306a36Sopenharmony_ci continue; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci rc = iort_id_map(map, node->type, map_id, &id, out_ref); 50062306a36Sopenharmony_ci if (!rc) 50162306a36Sopenharmony_ci break; 50262306a36Sopenharmony_ci if (rc == -EAGAIN) 50362306a36Sopenharmony_ci out_ref = map->output_reference; 50462306a36Sopenharmony_ci } 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci if (i == node->mapping_count && !out_ref) 50762306a36Sopenharmony_ci goto fail_map; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci node = ACPI_ADD_PTR(struct acpi_iort_node, iort_table, 51062306a36Sopenharmony_ci rc ? out_ref : map->output_reference); 51162306a36Sopenharmony_ci } 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_cifail_map: 51462306a36Sopenharmony_ci /* Map input ID to output ID unchanged on mapping failure */ 51562306a36Sopenharmony_ci if (id_out) 51662306a36Sopenharmony_ci *id_out = id_in; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci return NULL; 51962306a36Sopenharmony_ci} 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_cistatic struct acpi_iort_node *iort_node_map_platform_id( 52262306a36Sopenharmony_ci struct acpi_iort_node *node, u32 *id_out, u8 type_mask, 52362306a36Sopenharmony_ci int index) 52462306a36Sopenharmony_ci{ 52562306a36Sopenharmony_ci struct acpi_iort_node *parent; 52662306a36Sopenharmony_ci u32 id; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci /* step 1: retrieve the initial dev id */ 52962306a36Sopenharmony_ci parent = iort_node_get_id(node, &id, index); 53062306a36Sopenharmony_ci if (!parent) 53162306a36Sopenharmony_ci return NULL; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci /* 53462306a36Sopenharmony_ci * optional step 2: map the initial dev id if its parent is not 53562306a36Sopenharmony_ci * the target type we want, map it again for the use cases such 53662306a36Sopenharmony_ci * as NC (named component) -> SMMU -> ITS. If the type is matched, 53762306a36Sopenharmony_ci * return the initial dev id and its parent pointer directly. 53862306a36Sopenharmony_ci */ 53962306a36Sopenharmony_ci if (!(IORT_TYPE_MASK(parent->type) & type_mask)) 54062306a36Sopenharmony_ci parent = iort_node_map_id(parent, id, id_out, type_mask); 54162306a36Sopenharmony_ci else 54262306a36Sopenharmony_ci if (id_out) 54362306a36Sopenharmony_ci *id_out = id; 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci return parent; 54662306a36Sopenharmony_ci} 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_cistatic struct acpi_iort_node *iort_find_dev_node(struct device *dev) 54962306a36Sopenharmony_ci{ 55062306a36Sopenharmony_ci struct pci_bus *pbus; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci if (!dev_is_pci(dev)) { 55362306a36Sopenharmony_ci struct acpi_iort_node *node; 55462306a36Sopenharmony_ci /* 55562306a36Sopenharmony_ci * scan iort_fwnode_list to see if it's an iort platform 55662306a36Sopenharmony_ci * device (such as SMMU, PMCG),its iort node already cached 55762306a36Sopenharmony_ci * and associated with fwnode when iort platform devices 55862306a36Sopenharmony_ci * were initialized. 55962306a36Sopenharmony_ci */ 56062306a36Sopenharmony_ci node = iort_get_iort_node(dev->fwnode); 56162306a36Sopenharmony_ci if (node) 56262306a36Sopenharmony_ci return node; 56362306a36Sopenharmony_ci /* 56462306a36Sopenharmony_ci * if not, then it should be a platform device defined in 56562306a36Sopenharmony_ci * DSDT/SSDT (with Named Component node in IORT) 56662306a36Sopenharmony_ci */ 56762306a36Sopenharmony_ci return iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT, 56862306a36Sopenharmony_ci iort_match_node_callback, dev); 56962306a36Sopenharmony_ci } 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci pbus = to_pci_dev(dev)->bus; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci return iort_scan_node(ACPI_IORT_NODE_PCI_ROOT_COMPLEX, 57462306a36Sopenharmony_ci iort_match_node_callback, &pbus->dev); 57562306a36Sopenharmony_ci} 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci/** 57862306a36Sopenharmony_ci * iort_msi_map_id() - Map a MSI input ID for a device 57962306a36Sopenharmony_ci * @dev: The device for which the mapping is to be done. 58062306a36Sopenharmony_ci * @input_id: The device input ID. 58162306a36Sopenharmony_ci * 58262306a36Sopenharmony_ci * Returns: mapped MSI ID on success, input ID otherwise 58362306a36Sopenharmony_ci */ 58462306a36Sopenharmony_ciu32 iort_msi_map_id(struct device *dev, u32 input_id) 58562306a36Sopenharmony_ci{ 58662306a36Sopenharmony_ci struct acpi_iort_node *node; 58762306a36Sopenharmony_ci u32 dev_id; 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci node = iort_find_dev_node(dev); 59062306a36Sopenharmony_ci if (!node) 59162306a36Sopenharmony_ci return input_id; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci iort_node_map_id(node, input_id, &dev_id, IORT_MSI_TYPE); 59462306a36Sopenharmony_ci return dev_id; 59562306a36Sopenharmony_ci} 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci/** 59862306a36Sopenharmony_ci * iort_pmsi_get_dev_id() - Get the device id for a device 59962306a36Sopenharmony_ci * @dev: The device for which the mapping is to be done. 60062306a36Sopenharmony_ci * @dev_id: The device ID found. 60162306a36Sopenharmony_ci * 60262306a36Sopenharmony_ci * Returns: 0 for successful find a dev id, -ENODEV on error 60362306a36Sopenharmony_ci */ 60462306a36Sopenharmony_ciint iort_pmsi_get_dev_id(struct device *dev, u32 *dev_id) 60562306a36Sopenharmony_ci{ 60662306a36Sopenharmony_ci int i, index; 60762306a36Sopenharmony_ci struct acpi_iort_node *node; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci node = iort_find_dev_node(dev); 61062306a36Sopenharmony_ci if (!node) 61162306a36Sopenharmony_ci return -ENODEV; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci index = iort_get_id_mapping_index(node); 61462306a36Sopenharmony_ci /* if there is a valid index, go get the dev_id directly */ 61562306a36Sopenharmony_ci if (index >= 0) { 61662306a36Sopenharmony_ci if (iort_node_get_id(node, dev_id, index)) 61762306a36Sopenharmony_ci return 0; 61862306a36Sopenharmony_ci } else { 61962306a36Sopenharmony_ci for (i = 0; i < node->mapping_count; i++) { 62062306a36Sopenharmony_ci if (iort_node_map_platform_id(node, dev_id, 62162306a36Sopenharmony_ci IORT_MSI_TYPE, i)) 62262306a36Sopenharmony_ci return 0; 62362306a36Sopenharmony_ci } 62462306a36Sopenharmony_ci } 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci return -ENODEV; 62762306a36Sopenharmony_ci} 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_cistatic int __maybe_unused iort_find_its_base(u32 its_id, phys_addr_t *base) 63062306a36Sopenharmony_ci{ 63162306a36Sopenharmony_ci struct iort_its_msi_chip *its_msi_chip; 63262306a36Sopenharmony_ci int ret = -ENODEV; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci spin_lock(&iort_msi_chip_lock); 63562306a36Sopenharmony_ci list_for_each_entry(its_msi_chip, &iort_msi_chip_list, list) { 63662306a36Sopenharmony_ci if (its_msi_chip->translation_id == its_id) { 63762306a36Sopenharmony_ci *base = its_msi_chip->base_addr; 63862306a36Sopenharmony_ci ret = 0; 63962306a36Sopenharmony_ci break; 64062306a36Sopenharmony_ci } 64162306a36Sopenharmony_ci } 64262306a36Sopenharmony_ci spin_unlock(&iort_msi_chip_lock); 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci return ret; 64562306a36Sopenharmony_ci} 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci/** 64862306a36Sopenharmony_ci * iort_dev_find_its_id() - Find the ITS identifier for a device 64962306a36Sopenharmony_ci * @dev: The device. 65062306a36Sopenharmony_ci * @id: Device's ID 65162306a36Sopenharmony_ci * @idx: Index of the ITS identifier list. 65262306a36Sopenharmony_ci * @its_id: ITS identifier. 65362306a36Sopenharmony_ci * 65462306a36Sopenharmony_ci * Returns: 0 on success, appropriate error value otherwise 65562306a36Sopenharmony_ci */ 65662306a36Sopenharmony_cistatic int iort_dev_find_its_id(struct device *dev, u32 id, 65762306a36Sopenharmony_ci unsigned int idx, int *its_id) 65862306a36Sopenharmony_ci{ 65962306a36Sopenharmony_ci struct acpi_iort_its_group *its; 66062306a36Sopenharmony_ci struct acpi_iort_node *node; 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci node = iort_find_dev_node(dev); 66362306a36Sopenharmony_ci if (!node) 66462306a36Sopenharmony_ci return -ENXIO; 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci node = iort_node_map_id(node, id, NULL, IORT_MSI_TYPE); 66762306a36Sopenharmony_ci if (!node) 66862306a36Sopenharmony_ci return -ENXIO; 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci /* Move to ITS specific data */ 67162306a36Sopenharmony_ci its = (struct acpi_iort_its_group *)node->node_data; 67262306a36Sopenharmony_ci if (idx >= its->its_count) { 67362306a36Sopenharmony_ci dev_err(dev, "requested ITS ID index [%d] overruns ITS entries [%d]\n", 67462306a36Sopenharmony_ci idx, its->its_count); 67562306a36Sopenharmony_ci return -ENXIO; 67662306a36Sopenharmony_ci } 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci *its_id = its->identifiers[idx]; 67962306a36Sopenharmony_ci return 0; 68062306a36Sopenharmony_ci} 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci/** 68362306a36Sopenharmony_ci * iort_get_device_domain() - Find MSI domain related to a device 68462306a36Sopenharmony_ci * @dev: The device. 68562306a36Sopenharmony_ci * @id: Requester ID for the device. 68662306a36Sopenharmony_ci * @bus_token: irq domain bus token. 68762306a36Sopenharmony_ci * 68862306a36Sopenharmony_ci * Returns: the MSI domain for this device, NULL otherwise 68962306a36Sopenharmony_ci */ 69062306a36Sopenharmony_cistruct irq_domain *iort_get_device_domain(struct device *dev, u32 id, 69162306a36Sopenharmony_ci enum irq_domain_bus_token bus_token) 69262306a36Sopenharmony_ci{ 69362306a36Sopenharmony_ci struct fwnode_handle *handle; 69462306a36Sopenharmony_ci int its_id; 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci if (iort_dev_find_its_id(dev, id, 0, &its_id)) 69762306a36Sopenharmony_ci return NULL; 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci handle = iort_find_domain_token(its_id); 70062306a36Sopenharmony_ci if (!handle) 70162306a36Sopenharmony_ci return NULL; 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci return irq_find_matching_fwnode(handle, bus_token); 70462306a36Sopenharmony_ci} 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_cistatic void iort_set_device_domain(struct device *dev, 70762306a36Sopenharmony_ci struct acpi_iort_node *node) 70862306a36Sopenharmony_ci{ 70962306a36Sopenharmony_ci struct acpi_iort_its_group *its; 71062306a36Sopenharmony_ci struct acpi_iort_node *msi_parent; 71162306a36Sopenharmony_ci struct acpi_iort_id_mapping *map; 71262306a36Sopenharmony_ci struct fwnode_handle *iort_fwnode; 71362306a36Sopenharmony_ci struct irq_domain *domain; 71462306a36Sopenharmony_ci int index; 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci index = iort_get_id_mapping_index(node); 71762306a36Sopenharmony_ci if (index < 0) 71862306a36Sopenharmony_ci return; 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci map = ACPI_ADD_PTR(struct acpi_iort_id_mapping, node, 72162306a36Sopenharmony_ci node->mapping_offset + index * sizeof(*map)); 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci /* Firmware bug! */ 72462306a36Sopenharmony_ci if (!map->output_reference || 72562306a36Sopenharmony_ci !(map->flags & ACPI_IORT_ID_SINGLE_MAPPING)) { 72662306a36Sopenharmony_ci pr_err(FW_BUG "[node %p type %d] Invalid MSI mapping\n", 72762306a36Sopenharmony_ci node, node->type); 72862306a36Sopenharmony_ci return; 72962306a36Sopenharmony_ci } 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci msi_parent = ACPI_ADD_PTR(struct acpi_iort_node, iort_table, 73262306a36Sopenharmony_ci map->output_reference); 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci if (!msi_parent || msi_parent->type != ACPI_IORT_NODE_ITS_GROUP) 73562306a36Sopenharmony_ci return; 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci /* Move to ITS specific data */ 73862306a36Sopenharmony_ci its = (struct acpi_iort_its_group *)msi_parent->node_data; 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci iort_fwnode = iort_find_domain_token(its->identifiers[0]); 74162306a36Sopenharmony_ci if (!iort_fwnode) 74262306a36Sopenharmony_ci return; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci domain = irq_find_matching_fwnode(iort_fwnode, DOMAIN_BUS_PLATFORM_MSI); 74562306a36Sopenharmony_ci if (domain) 74662306a36Sopenharmony_ci dev_set_msi_domain(dev, domain); 74762306a36Sopenharmony_ci} 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci/** 75062306a36Sopenharmony_ci * iort_get_platform_device_domain() - Find MSI domain related to a 75162306a36Sopenharmony_ci * platform device 75262306a36Sopenharmony_ci * @dev: the dev pointer associated with the platform device 75362306a36Sopenharmony_ci * 75462306a36Sopenharmony_ci * Returns: the MSI domain for this device, NULL otherwise 75562306a36Sopenharmony_ci */ 75662306a36Sopenharmony_cistatic struct irq_domain *iort_get_platform_device_domain(struct device *dev) 75762306a36Sopenharmony_ci{ 75862306a36Sopenharmony_ci struct acpi_iort_node *node, *msi_parent = NULL; 75962306a36Sopenharmony_ci struct fwnode_handle *iort_fwnode; 76062306a36Sopenharmony_ci struct acpi_iort_its_group *its; 76162306a36Sopenharmony_ci int i; 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci /* find its associated iort node */ 76462306a36Sopenharmony_ci node = iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT, 76562306a36Sopenharmony_ci iort_match_node_callback, dev); 76662306a36Sopenharmony_ci if (!node) 76762306a36Sopenharmony_ci return NULL; 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci /* then find its msi parent node */ 77062306a36Sopenharmony_ci for (i = 0; i < node->mapping_count; i++) { 77162306a36Sopenharmony_ci msi_parent = iort_node_map_platform_id(node, NULL, 77262306a36Sopenharmony_ci IORT_MSI_TYPE, i); 77362306a36Sopenharmony_ci if (msi_parent) 77462306a36Sopenharmony_ci break; 77562306a36Sopenharmony_ci } 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci if (!msi_parent) 77862306a36Sopenharmony_ci return NULL; 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci /* Move to ITS specific data */ 78162306a36Sopenharmony_ci its = (struct acpi_iort_its_group *)msi_parent->node_data; 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci iort_fwnode = iort_find_domain_token(its->identifiers[0]); 78462306a36Sopenharmony_ci if (!iort_fwnode) 78562306a36Sopenharmony_ci return NULL; 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci return irq_find_matching_fwnode(iort_fwnode, DOMAIN_BUS_PLATFORM_MSI); 78862306a36Sopenharmony_ci} 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_civoid acpi_configure_pmsi_domain(struct device *dev) 79162306a36Sopenharmony_ci{ 79262306a36Sopenharmony_ci struct irq_domain *msi_domain; 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci msi_domain = iort_get_platform_device_domain(dev); 79562306a36Sopenharmony_ci if (msi_domain) 79662306a36Sopenharmony_ci dev_set_msi_domain(dev, msi_domain); 79762306a36Sopenharmony_ci} 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci#ifdef CONFIG_IOMMU_API 80062306a36Sopenharmony_cistatic void iort_rmr_free(struct device *dev, 80162306a36Sopenharmony_ci struct iommu_resv_region *region) 80262306a36Sopenharmony_ci{ 80362306a36Sopenharmony_ci struct iommu_iort_rmr_data *rmr_data; 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci rmr_data = container_of(region, struct iommu_iort_rmr_data, rr); 80662306a36Sopenharmony_ci kfree(rmr_data->sids); 80762306a36Sopenharmony_ci kfree(rmr_data); 80862306a36Sopenharmony_ci} 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_cistatic struct iommu_iort_rmr_data *iort_rmr_alloc( 81162306a36Sopenharmony_ci struct acpi_iort_rmr_desc *rmr_desc, 81262306a36Sopenharmony_ci int prot, enum iommu_resv_type type, 81362306a36Sopenharmony_ci u32 *sids, u32 num_sids) 81462306a36Sopenharmony_ci{ 81562306a36Sopenharmony_ci struct iommu_iort_rmr_data *rmr_data; 81662306a36Sopenharmony_ci struct iommu_resv_region *region; 81762306a36Sopenharmony_ci u32 *sids_copy; 81862306a36Sopenharmony_ci u64 addr = rmr_desc->base_address, size = rmr_desc->length; 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci rmr_data = kmalloc(sizeof(*rmr_data), GFP_KERNEL); 82162306a36Sopenharmony_ci if (!rmr_data) 82262306a36Sopenharmony_ci return NULL; 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci /* Create a copy of SIDs array to associate with this rmr_data */ 82562306a36Sopenharmony_ci sids_copy = kmemdup(sids, num_sids * sizeof(*sids), GFP_KERNEL); 82662306a36Sopenharmony_ci if (!sids_copy) { 82762306a36Sopenharmony_ci kfree(rmr_data); 82862306a36Sopenharmony_ci return NULL; 82962306a36Sopenharmony_ci } 83062306a36Sopenharmony_ci rmr_data->sids = sids_copy; 83162306a36Sopenharmony_ci rmr_data->num_sids = num_sids; 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci if (!IS_ALIGNED(addr, SZ_64K) || !IS_ALIGNED(size, SZ_64K)) { 83462306a36Sopenharmony_ci /* PAGE align base addr and size */ 83562306a36Sopenharmony_ci addr &= PAGE_MASK; 83662306a36Sopenharmony_ci size = PAGE_ALIGN(size + offset_in_page(rmr_desc->base_address)); 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci pr_err(FW_BUG "RMR descriptor[0x%llx - 0x%llx] not aligned to 64K, continue with [0x%llx - 0x%llx]\n", 83962306a36Sopenharmony_ci rmr_desc->base_address, 84062306a36Sopenharmony_ci rmr_desc->base_address + rmr_desc->length - 1, 84162306a36Sopenharmony_ci addr, addr + size - 1); 84262306a36Sopenharmony_ci } 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci region = &rmr_data->rr; 84562306a36Sopenharmony_ci INIT_LIST_HEAD(®ion->list); 84662306a36Sopenharmony_ci region->start = addr; 84762306a36Sopenharmony_ci region->length = size; 84862306a36Sopenharmony_ci region->prot = prot; 84962306a36Sopenharmony_ci region->type = type; 85062306a36Sopenharmony_ci region->free = iort_rmr_free; 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci return rmr_data; 85362306a36Sopenharmony_ci} 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_cistatic void iort_rmr_desc_check_overlap(struct acpi_iort_rmr_desc *desc, 85662306a36Sopenharmony_ci u32 count) 85762306a36Sopenharmony_ci{ 85862306a36Sopenharmony_ci int i, j; 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci for (i = 0; i < count; i++) { 86162306a36Sopenharmony_ci u64 end, start = desc[i].base_address, length = desc[i].length; 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci if (!length) { 86462306a36Sopenharmony_ci pr_err(FW_BUG "RMR descriptor[0x%llx] with zero length, continue anyway\n", 86562306a36Sopenharmony_ci start); 86662306a36Sopenharmony_ci continue; 86762306a36Sopenharmony_ci } 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci end = start + length - 1; 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci /* Check for address overlap */ 87262306a36Sopenharmony_ci for (j = i + 1; j < count; j++) { 87362306a36Sopenharmony_ci u64 e_start = desc[j].base_address; 87462306a36Sopenharmony_ci u64 e_end = e_start + desc[j].length - 1; 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci if (start <= e_end && end >= e_start) 87762306a36Sopenharmony_ci pr_err(FW_BUG "RMR descriptor[0x%llx - 0x%llx] overlaps, continue anyway\n", 87862306a36Sopenharmony_ci start, end); 87962306a36Sopenharmony_ci } 88062306a36Sopenharmony_ci } 88162306a36Sopenharmony_ci} 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci/* 88462306a36Sopenharmony_ci * Please note, we will keep the already allocated RMR reserve 88562306a36Sopenharmony_ci * regions in case of a memory allocation failure. 88662306a36Sopenharmony_ci */ 88762306a36Sopenharmony_cistatic void iort_get_rmrs(struct acpi_iort_node *node, 88862306a36Sopenharmony_ci struct acpi_iort_node *smmu, 88962306a36Sopenharmony_ci u32 *sids, u32 num_sids, 89062306a36Sopenharmony_ci struct list_head *head) 89162306a36Sopenharmony_ci{ 89262306a36Sopenharmony_ci struct acpi_iort_rmr *rmr = (struct acpi_iort_rmr *)node->node_data; 89362306a36Sopenharmony_ci struct acpi_iort_rmr_desc *rmr_desc; 89462306a36Sopenharmony_ci int i; 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci rmr_desc = ACPI_ADD_PTR(struct acpi_iort_rmr_desc, node, 89762306a36Sopenharmony_ci rmr->rmr_offset); 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci iort_rmr_desc_check_overlap(rmr_desc, rmr->rmr_count); 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci for (i = 0; i < rmr->rmr_count; i++, rmr_desc++) { 90262306a36Sopenharmony_ci struct iommu_iort_rmr_data *rmr_data; 90362306a36Sopenharmony_ci enum iommu_resv_type type; 90462306a36Sopenharmony_ci int prot = IOMMU_READ | IOMMU_WRITE; 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_ci if (rmr->flags & ACPI_IORT_RMR_REMAP_PERMITTED) 90762306a36Sopenharmony_ci type = IOMMU_RESV_DIRECT_RELAXABLE; 90862306a36Sopenharmony_ci else 90962306a36Sopenharmony_ci type = IOMMU_RESV_DIRECT; 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci if (rmr->flags & ACPI_IORT_RMR_ACCESS_PRIVILEGE) 91262306a36Sopenharmony_ci prot |= IOMMU_PRIV; 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci /* Attributes 0x00 - 0x03 represents device memory */ 91562306a36Sopenharmony_ci if (ACPI_IORT_RMR_ACCESS_ATTRIBUTES(rmr->flags) <= 91662306a36Sopenharmony_ci ACPI_IORT_RMR_ATTR_DEVICE_GRE) 91762306a36Sopenharmony_ci prot |= IOMMU_MMIO; 91862306a36Sopenharmony_ci else if (ACPI_IORT_RMR_ACCESS_ATTRIBUTES(rmr->flags) == 91962306a36Sopenharmony_ci ACPI_IORT_RMR_ATTR_NORMAL_IWB_OWB) 92062306a36Sopenharmony_ci prot |= IOMMU_CACHE; 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci rmr_data = iort_rmr_alloc(rmr_desc, prot, type, 92362306a36Sopenharmony_ci sids, num_sids); 92462306a36Sopenharmony_ci if (!rmr_data) 92562306a36Sopenharmony_ci return; 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci list_add_tail(&rmr_data->rr.list, head); 92862306a36Sopenharmony_ci } 92962306a36Sopenharmony_ci} 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_cistatic u32 *iort_rmr_alloc_sids(u32 *sids, u32 count, u32 id_start, 93262306a36Sopenharmony_ci u32 new_count) 93362306a36Sopenharmony_ci{ 93462306a36Sopenharmony_ci u32 *new_sids; 93562306a36Sopenharmony_ci u32 total_count = count + new_count; 93662306a36Sopenharmony_ci int i; 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci new_sids = krealloc_array(sids, count + new_count, 93962306a36Sopenharmony_ci sizeof(*new_sids), GFP_KERNEL); 94062306a36Sopenharmony_ci if (!new_sids) 94162306a36Sopenharmony_ci return NULL; 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci for (i = count; i < total_count; i++) 94462306a36Sopenharmony_ci new_sids[i] = id_start++; 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci return new_sids; 94762306a36Sopenharmony_ci} 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_cistatic bool iort_rmr_has_dev(struct device *dev, u32 id_start, 95062306a36Sopenharmony_ci u32 id_count) 95162306a36Sopenharmony_ci{ 95262306a36Sopenharmony_ci int i; 95362306a36Sopenharmony_ci struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci /* 95662306a36Sopenharmony_ci * Make sure the kernel has preserved the boot firmware PCIe 95762306a36Sopenharmony_ci * configuration. This is required to ensure that the RMR PCIe 95862306a36Sopenharmony_ci * StreamIDs are still valid (Refer: ARM DEN 0049E.d Section 3.1.1.5). 95962306a36Sopenharmony_ci */ 96062306a36Sopenharmony_ci if (dev_is_pci(dev)) { 96162306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 96262306a36Sopenharmony_ci struct pci_host_bridge *host = pci_find_host_bridge(pdev->bus); 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci if (!host->preserve_config) 96562306a36Sopenharmony_ci return false; 96662306a36Sopenharmony_ci } 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci for (i = 0; i < fwspec->num_ids; i++) { 96962306a36Sopenharmony_ci if (fwspec->ids[i] >= id_start && 97062306a36Sopenharmony_ci fwspec->ids[i] <= id_start + id_count) 97162306a36Sopenharmony_ci return true; 97262306a36Sopenharmony_ci } 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci return false; 97562306a36Sopenharmony_ci} 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_cistatic void iort_node_get_rmr_info(struct acpi_iort_node *node, 97862306a36Sopenharmony_ci struct acpi_iort_node *iommu, 97962306a36Sopenharmony_ci struct device *dev, struct list_head *head) 98062306a36Sopenharmony_ci{ 98162306a36Sopenharmony_ci struct acpi_iort_node *smmu = NULL; 98262306a36Sopenharmony_ci struct acpi_iort_rmr *rmr; 98362306a36Sopenharmony_ci struct acpi_iort_id_mapping *map; 98462306a36Sopenharmony_ci u32 *sids = NULL; 98562306a36Sopenharmony_ci u32 num_sids = 0; 98662306a36Sopenharmony_ci int i; 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci if (!node->mapping_offset || !node->mapping_count) { 98962306a36Sopenharmony_ci pr_err(FW_BUG "Invalid ID mapping, skipping RMR node %p\n", 99062306a36Sopenharmony_ci node); 99162306a36Sopenharmony_ci return; 99262306a36Sopenharmony_ci } 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci rmr = (struct acpi_iort_rmr *)node->node_data; 99562306a36Sopenharmony_ci if (!rmr->rmr_offset || !rmr->rmr_count) 99662306a36Sopenharmony_ci return; 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci map = ACPI_ADD_PTR(struct acpi_iort_id_mapping, node, 99962306a36Sopenharmony_ci node->mapping_offset); 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci /* 100262306a36Sopenharmony_ci * Go through the ID mappings and see if we have a match for SMMU 100362306a36Sopenharmony_ci * and dev(if !NULL). If found, get the sids for the Node. 100462306a36Sopenharmony_ci * Please note, id_count is equal to the number of IDs in the 100562306a36Sopenharmony_ci * range minus one. 100662306a36Sopenharmony_ci */ 100762306a36Sopenharmony_ci for (i = 0; i < node->mapping_count; i++, map++) { 100862306a36Sopenharmony_ci struct acpi_iort_node *parent; 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_ci parent = ACPI_ADD_PTR(struct acpi_iort_node, iort_table, 101162306a36Sopenharmony_ci map->output_reference); 101262306a36Sopenharmony_ci if (parent != iommu) 101362306a36Sopenharmony_ci continue; 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci /* If dev is valid, check RMR node corresponds to the dev SID */ 101662306a36Sopenharmony_ci if (dev && !iort_rmr_has_dev(dev, map->output_base, 101762306a36Sopenharmony_ci map->id_count)) 101862306a36Sopenharmony_ci continue; 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci /* Retrieve SIDs associated with the Node. */ 102162306a36Sopenharmony_ci sids = iort_rmr_alloc_sids(sids, num_sids, map->output_base, 102262306a36Sopenharmony_ci map->id_count + 1); 102362306a36Sopenharmony_ci if (!sids) 102462306a36Sopenharmony_ci return; 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci num_sids += map->id_count + 1; 102762306a36Sopenharmony_ci } 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci if (!sids) 103062306a36Sopenharmony_ci return; 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_ci iort_get_rmrs(node, smmu, sids, num_sids, head); 103362306a36Sopenharmony_ci kfree(sids); 103462306a36Sopenharmony_ci} 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_cistatic void iort_find_rmrs(struct acpi_iort_node *iommu, struct device *dev, 103762306a36Sopenharmony_ci struct list_head *head) 103862306a36Sopenharmony_ci{ 103962306a36Sopenharmony_ci struct acpi_table_iort *iort; 104062306a36Sopenharmony_ci struct acpi_iort_node *iort_node, *iort_end; 104162306a36Sopenharmony_ci int i; 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_ci /* Only supports ARM DEN 0049E.d onwards */ 104462306a36Sopenharmony_ci if (iort_table->revision < 5) 104562306a36Sopenharmony_ci return; 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_ci iort = (struct acpi_table_iort *)iort_table; 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort, 105062306a36Sopenharmony_ci iort->node_offset); 105162306a36Sopenharmony_ci iort_end = ACPI_ADD_PTR(struct acpi_iort_node, iort, 105262306a36Sopenharmony_ci iort_table->length); 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_ci for (i = 0; i < iort->node_count; i++) { 105562306a36Sopenharmony_ci if (WARN_TAINT(iort_node >= iort_end, TAINT_FIRMWARE_WORKAROUND, 105662306a36Sopenharmony_ci "IORT node pointer overflows, bad table!\n")) 105762306a36Sopenharmony_ci return; 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci if (iort_node->type == ACPI_IORT_NODE_RMR) 106062306a36Sopenharmony_ci iort_node_get_rmr_info(iort_node, iommu, dev, head); 106162306a36Sopenharmony_ci 106262306a36Sopenharmony_ci iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort_node, 106362306a36Sopenharmony_ci iort_node->length); 106462306a36Sopenharmony_ci } 106562306a36Sopenharmony_ci} 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci/* 106862306a36Sopenharmony_ci * Populate the RMR list associated with a given IOMMU and dev(if provided). 106962306a36Sopenharmony_ci * If dev is NULL, the function populates all the RMRs associated with the 107062306a36Sopenharmony_ci * given IOMMU. 107162306a36Sopenharmony_ci */ 107262306a36Sopenharmony_cistatic void iort_iommu_rmr_get_resv_regions(struct fwnode_handle *iommu_fwnode, 107362306a36Sopenharmony_ci struct device *dev, 107462306a36Sopenharmony_ci struct list_head *head) 107562306a36Sopenharmony_ci{ 107662306a36Sopenharmony_ci struct acpi_iort_node *iommu; 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_ci iommu = iort_get_iort_node(iommu_fwnode); 107962306a36Sopenharmony_ci if (!iommu) 108062306a36Sopenharmony_ci return; 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_ci iort_find_rmrs(iommu, dev, head); 108362306a36Sopenharmony_ci} 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_cistatic struct acpi_iort_node *iort_get_msi_resv_iommu(struct device *dev) 108662306a36Sopenharmony_ci{ 108762306a36Sopenharmony_ci struct acpi_iort_node *iommu; 108862306a36Sopenharmony_ci struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_ci iommu = iort_get_iort_node(fwspec->iommu_fwnode); 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci if (iommu && (iommu->type == ACPI_IORT_NODE_SMMU_V3)) { 109362306a36Sopenharmony_ci struct acpi_iort_smmu_v3 *smmu; 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci smmu = (struct acpi_iort_smmu_v3 *)iommu->node_data; 109662306a36Sopenharmony_ci if (smmu->model == ACPI_IORT_SMMU_V3_HISILICON_HI161X) 109762306a36Sopenharmony_ci return iommu; 109862306a36Sopenharmony_ci } 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_ci return NULL; 110162306a36Sopenharmony_ci} 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_ci/* 110462306a36Sopenharmony_ci * Retrieve platform specific HW MSI reserve regions. 110562306a36Sopenharmony_ci * The ITS interrupt translation spaces (ITS_base + SZ_64K, SZ_64K) 110662306a36Sopenharmony_ci * associated with the device are the HW MSI reserved regions. 110762306a36Sopenharmony_ci */ 110862306a36Sopenharmony_cistatic void iort_iommu_msi_get_resv_regions(struct device *dev, 110962306a36Sopenharmony_ci struct list_head *head) 111062306a36Sopenharmony_ci{ 111162306a36Sopenharmony_ci struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); 111262306a36Sopenharmony_ci struct acpi_iort_its_group *its; 111362306a36Sopenharmony_ci struct acpi_iort_node *iommu_node, *its_node = NULL; 111462306a36Sopenharmony_ci int i; 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci iommu_node = iort_get_msi_resv_iommu(dev); 111762306a36Sopenharmony_ci if (!iommu_node) 111862306a36Sopenharmony_ci return; 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_ci /* 112162306a36Sopenharmony_ci * Current logic to reserve ITS regions relies on HW topologies 112262306a36Sopenharmony_ci * where a given PCI or named component maps its IDs to only one 112362306a36Sopenharmony_ci * ITS group; if a PCI or named component can map its IDs to 112462306a36Sopenharmony_ci * different ITS groups through IORT mappings this function has 112562306a36Sopenharmony_ci * to be reworked to ensure we reserve regions for all ITS groups 112662306a36Sopenharmony_ci * a given PCI or named component may map IDs to. 112762306a36Sopenharmony_ci */ 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_ci for (i = 0; i < fwspec->num_ids; i++) { 113062306a36Sopenharmony_ci its_node = iort_node_map_id(iommu_node, 113162306a36Sopenharmony_ci fwspec->ids[i], 113262306a36Sopenharmony_ci NULL, IORT_MSI_TYPE); 113362306a36Sopenharmony_ci if (its_node) 113462306a36Sopenharmony_ci break; 113562306a36Sopenharmony_ci } 113662306a36Sopenharmony_ci 113762306a36Sopenharmony_ci if (!its_node) 113862306a36Sopenharmony_ci return; 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ci /* Move to ITS specific data */ 114162306a36Sopenharmony_ci its = (struct acpi_iort_its_group *)its_node->node_data; 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci for (i = 0; i < its->its_count; i++) { 114462306a36Sopenharmony_ci phys_addr_t base; 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_ci if (!iort_find_its_base(its->identifiers[i], &base)) { 114762306a36Sopenharmony_ci int prot = IOMMU_WRITE | IOMMU_NOEXEC | IOMMU_MMIO; 114862306a36Sopenharmony_ci struct iommu_resv_region *region; 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_ci region = iommu_alloc_resv_region(base + SZ_64K, SZ_64K, 115162306a36Sopenharmony_ci prot, IOMMU_RESV_MSI, 115262306a36Sopenharmony_ci GFP_KERNEL); 115362306a36Sopenharmony_ci if (region) 115462306a36Sopenharmony_ci list_add_tail(®ion->list, head); 115562306a36Sopenharmony_ci } 115662306a36Sopenharmony_ci } 115762306a36Sopenharmony_ci} 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ci/** 116062306a36Sopenharmony_ci * iort_iommu_get_resv_regions - Generic helper to retrieve reserved regions. 116162306a36Sopenharmony_ci * @dev: Device from iommu_get_resv_regions() 116262306a36Sopenharmony_ci * @head: Reserved region list from iommu_get_resv_regions() 116362306a36Sopenharmony_ci */ 116462306a36Sopenharmony_civoid iort_iommu_get_resv_regions(struct device *dev, struct list_head *head) 116562306a36Sopenharmony_ci{ 116662306a36Sopenharmony_ci struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_ci iort_iommu_msi_get_resv_regions(dev, head); 116962306a36Sopenharmony_ci iort_iommu_rmr_get_resv_regions(fwspec->iommu_fwnode, dev, head); 117062306a36Sopenharmony_ci} 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci/** 117362306a36Sopenharmony_ci * iort_get_rmr_sids - Retrieve IORT RMR node reserved regions with 117462306a36Sopenharmony_ci * associated StreamIDs information. 117562306a36Sopenharmony_ci * @iommu_fwnode: fwnode associated with IOMMU 117662306a36Sopenharmony_ci * @head: Resereved region list 117762306a36Sopenharmony_ci */ 117862306a36Sopenharmony_civoid iort_get_rmr_sids(struct fwnode_handle *iommu_fwnode, 117962306a36Sopenharmony_ci struct list_head *head) 118062306a36Sopenharmony_ci{ 118162306a36Sopenharmony_ci iort_iommu_rmr_get_resv_regions(iommu_fwnode, NULL, head); 118262306a36Sopenharmony_ci} 118362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iort_get_rmr_sids); 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci/** 118662306a36Sopenharmony_ci * iort_put_rmr_sids - Free memory allocated for RMR reserved regions. 118762306a36Sopenharmony_ci * @iommu_fwnode: fwnode associated with IOMMU 118862306a36Sopenharmony_ci * @head: Resereved region list 118962306a36Sopenharmony_ci */ 119062306a36Sopenharmony_civoid iort_put_rmr_sids(struct fwnode_handle *iommu_fwnode, 119162306a36Sopenharmony_ci struct list_head *head) 119262306a36Sopenharmony_ci{ 119362306a36Sopenharmony_ci struct iommu_resv_region *entry, *next; 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_ci list_for_each_entry_safe(entry, next, head, list) 119662306a36Sopenharmony_ci entry->free(NULL, entry); 119762306a36Sopenharmony_ci} 119862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iort_put_rmr_sids); 119962306a36Sopenharmony_ci 120062306a36Sopenharmony_cistatic inline bool iort_iommu_driver_enabled(u8 type) 120162306a36Sopenharmony_ci{ 120262306a36Sopenharmony_ci switch (type) { 120362306a36Sopenharmony_ci case ACPI_IORT_NODE_SMMU_V3: 120462306a36Sopenharmony_ci return IS_ENABLED(CONFIG_ARM_SMMU_V3); 120562306a36Sopenharmony_ci case ACPI_IORT_NODE_SMMU: 120662306a36Sopenharmony_ci return IS_ENABLED(CONFIG_ARM_SMMU); 120762306a36Sopenharmony_ci default: 120862306a36Sopenharmony_ci pr_warn("IORT node type %u does not describe an SMMU\n", type); 120962306a36Sopenharmony_ci return false; 121062306a36Sopenharmony_ci } 121162306a36Sopenharmony_ci} 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_cistatic bool iort_pci_rc_supports_ats(struct acpi_iort_node *node) 121462306a36Sopenharmony_ci{ 121562306a36Sopenharmony_ci struct acpi_iort_root_complex *pci_rc; 121662306a36Sopenharmony_ci 121762306a36Sopenharmony_ci pci_rc = (struct acpi_iort_root_complex *)node->node_data; 121862306a36Sopenharmony_ci return pci_rc->ats_attribute & ACPI_IORT_ATS_SUPPORTED; 121962306a36Sopenharmony_ci} 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_cistatic int iort_iommu_xlate(struct device *dev, struct acpi_iort_node *node, 122262306a36Sopenharmony_ci u32 streamid) 122362306a36Sopenharmony_ci{ 122462306a36Sopenharmony_ci const struct iommu_ops *ops; 122562306a36Sopenharmony_ci struct fwnode_handle *iort_fwnode; 122662306a36Sopenharmony_ci 122762306a36Sopenharmony_ci if (!node) 122862306a36Sopenharmony_ci return -ENODEV; 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_ci iort_fwnode = iort_get_fwnode(node); 123162306a36Sopenharmony_ci if (!iort_fwnode) 123262306a36Sopenharmony_ci return -ENODEV; 123362306a36Sopenharmony_ci 123462306a36Sopenharmony_ci /* 123562306a36Sopenharmony_ci * If the ops look-up fails, this means that either 123662306a36Sopenharmony_ci * the SMMU drivers have not been probed yet or that 123762306a36Sopenharmony_ci * the SMMU drivers are not built in the kernel; 123862306a36Sopenharmony_ci * Depending on whether the SMMU drivers are built-in 123962306a36Sopenharmony_ci * in the kernel or not, defer the IOMMU configuration 124062306a36Sopenharmony_ci * or just abort it. 124162306a36Sopenharmony_ci */ 124262306a36Sopenharmony_ci ops = iommu_ops_from_fwnode(iort_fwnode); 124362306a36Sopenharmony_ci if (!ops) 124462306a36Sopenharmony_ci return iort_iommu_driver_enabled(node->type) ? 124562306a36Sopenharmony_ci -EPROBE_DEFER : -ENODEV; 124662306a36Sopenharmony_ci 124762306a36Sopenharmony_ci return acpi_iommu_fwspec_init(dev, streamid, iort_fwnode, ops); 124862306a36Sopenharmony_ci} 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_cistruct iort_pci_alias_info { 125162306a36Sopenharmony_ci struct device *dev; 125262306a36Sopenharmony_ci struct acpi_iort_node *node; 125362306a36Sopenharmony_ci}; 125462306a36Sopenharmony_ci 125562306a36Sopenharmony_cistatic int iort_pci_iommu_init(struct pci_dev *pdev, u16 alias, void *data) 125662306a36Sopenharmony_ci{ 125762306a36Sopenharmony_ci struct iort_pci_alias_info *info = data; 125862306a36Sopenharmony_ci struct acpi_iort_node *parent; 125962306a36Sopenharmony_ci u32 streamid; 126062306a36Sopenharmony_ci 126162306a36Sopenharmony_ci parent = iort_node_map_id(info->node, alias, &streamid, 126262306a36Sopenharmony_ci IORT_IOMMU_TYPE); 126362306a36Sopenharmony_ci return iort_iommu_xlate(info->dev, parent, streamid); 126462306a36Sopenharmony_ci} 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_cistatic void iort_named_component_init(struct device *dev, 126762306a36Sopenharmony_ci struct acpi_iort_node *node) 126862306a36Sopenharmony_ci{ 126962306a36Sopenharmony_ci struct property_entry props[3] = {}; 127062306a36Sopenharmony_ci struct acpi_iort_named_component *nc; 127162306a36Sopenharmony_ci 127262306a36Sopenharmony_ci nc = (struct acpi_iort_named_component *)node->node_data; 127362306a36Sopenharmony_ci props[0] = PROPERTY_ENTRY_U32("pasid-num-bits", 127462306a36Sopenharmony_ci FIELD_GET(ACPI_IORT_NC_PASID_BITS, 127562306a36Sopenharmony_ci nc->node_flags)); 127662306a36Sopenharmony_ci if (nc->node_flags & ACPI_IORT_NC_STALL_SUPPORTED) 127762306a36Sopenharmony_ci props[1] = PROPERTY_ENTRY_BOOL("dma-can-stall"); 127862306a36Sopenharmony_ci 127962306a36Sopenharmony_ci if (device_create_managed_software_node(dev, props, NULL)) 128062306a36Sopenharmony_ci dev_warn(dev, "Could not add device properties\n"); 128162306a36Sopenharmony_ci} 128262306a36Sopenharmony_ci 128362306a36Sopenharmony_cistatic int iort_nc_iommu_map(struct device *dev, struct acpi_iort_node *node) 128462306a36Sopenharmony_ci{ 128562306a36Sopenharmony_ci struct acpi_iort_node *parent; 128662306a36Sopenharmony_ci int err = -ENODEV, i = 0; 128762306a36Sopenharmony_ci u32 streamid = 0; 128862306a36Sopenharmony_ci 128962306a36Sopenharmony_ci do { 129062306a36Sopenharmony_ci 129162306a36Sopenharmony_ci parent = iort_node_map_platform_id(node, &streamid, 129262306a36Sopenharmony_ci IORT_IOMMU_TYPE, 129362306a36Sopenharmony_ci i++); 129462306a36Sopenharmony_ci 129562306a36Sopenharmony_ci if (parent) 129662306a36Sopenharmony_ci err = iort_iommu_xlate(dev, parent, streamid); 129762306a36Sopenharmony_ci } while (parent && !err); 129862306a36Sopenharmony_ci 129962306a36Sopenharmony_ci return err; 130062306a36Sopenharmony_ci} 130162306a36Sopenharmony_ci 130262306a36Sopenharmony_cistatic int iort_nc_iommu_map_id(struct device *dev, 130362306a36Sopenharmony_ci struct acpi_iort_node *node, 130462306a36Sopenharmony_ci const u32 *in_id) 130562306a36Sopenharmony_ci{ 130662306a36Sopenharmony_ci struct acpi_iort_node *parent; 130762306a36Sopenharmony_ci u32 streamid; 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_ci parent = iort_node_map_id(node, *in_id, &streamid, IORT_IOMMU_TYPE); 131062306a36Sopenharmony_ci if (parent) 131162306a36Sopenharmony_ci return iort_iommu_xlate(dev, parent, streamid); 131262306a36Sopenharmony_ci 131362306a36Sopenharmony_ci return -ENODEV; 131462306a36Sopenharmony_ci} 131562306a36Sopenharmony_ci 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_ci/** 131862306a36Sopenharmony_ci * iort_iommu_configure_id - Set-up IOMMU configuration for a device. 131962306a36Sopenharmony_ci * 132062306a36Sopenharmony_ci * @dev: device to configure 132162306a36Sopenharmony_ci * @id_in: optional input id const value pointer 132262306a36Sopenharmony_ci * 132362306a36Sopenharmony_ci * Returns: 0 on success, <0 on failure 132462306a36Sopenharmony_ci */ 132562306a36Sopenharmony_ciint iort_iommu_configure_id(struct device *dev, const u32 *id_in) 132662306a36Sopenharmony_ci{ 132762306a36Sopenharmony_ci struct acpi_iort_node *node; 132862306a36Sopenharmony_ci int err = -ENODEV; 132962306a36Sopenharmony_ci 133062306a36Sopenharmony_ci if (dev_is_pci(dev)) { 133162306a36Sopenharmony_ci struct iommu_fwspec *fwspec; 133262306a36Sopenharmony_ci struct pci_bus *bus = to_pci_dev(dev)->bus; 133362306a36Sopenharmony_ci struct iort_pci_alias_info info = { .dev = dev }; 133462306a36Sopenharmony_ci 133562306a36Sopenharmony_ci node = iort_scan_node(ACPI_IORT_NODE_PCI_ROOT_COMPLEX, 133662306a36Sopenharmony_ci iort_match_node_callback, &bus->dev); 133762306a36Sopenharmony_ci if (!node) 133862306a36Sopenharmony_ci return -ENODEV; 133962306a36Sopenharmony_ci 134062306a36Sopenharmony_ci info.node = node; 134162306a36Sopenharmony_ci err = pci_for_each_dma_alias(to_pci_dev(dev), 134262306a36Sopenharmony_ci iort_pci_iommu_init, &info); 134362306a36Sopenharmony_ci 134462306a36Sopenharmony_ci fwspec = dev_iommu_fwspec_get(dev); 134562306a36Sopenharmony_ci if (fwspec && iort_pci_rc_supports_ats(node)) 134662306a36Sopenharmony_ci fwspec->flags |= IOMMU_FWSPEC_PCI_RC_ATS; 134762306a36Sopenharmony_ci } else { 134862306a36Sopenharmony_ci node = iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT, 134962306a36Sopenharmony_ci iort_match_node_callback, dev); 135062306a36Sopenharmony_ci if (!node) 135162306a36Sopenharmony_ci return -ENODEV; 135262306a36Sopenharmony_ci 135362306a36Sopenharmony_ci err = id_in ? iort_nc_iommu_map_id(dev, node, id_in) : 135462306a36Sopenharmony_ci iort_nc_iommu_map(dev, node); 135562306a36Sopenharmony_ci 135662306a36Sopenharmony_ci if (!err) 135762306a36Sopenharmony_ci iort_named_component_init(dev, node); 135862306a36Sopenharmony_ci } 135962306a36Sopenharmony_ci 136062306a36Sopenharmony_ci return err; 136162306a36Sopenharmony_ci} 136262306a36Sopenharmony_ci 136362306a36Sopenharmony_ci#else 136462306a36Sopenharmony_civoid iort_iommu_get_resv_regions(struct device *dev, struct list_head *head) 136562306a36Sopenharmony_ci{ } 136662306a36Sopenharmony_ciint iort_iommu_configure_id(struct device *dev, const u32 *input_id) 136762306a36Sopenharmony_ci{ return -ENODEV; } 136862306a36Sopenharmony_ci#endif 136962306a36Sopenharmony_ci 137062306a36Sopenharmony_cistatic int nc_dma_get_range(struct device *dev, u64 *size) 137162306a36Sopenharmony_ci{ 137262306a36Sopenharmony_ci struct acpi_iort_node *node; 137362306a36Sopenharmony_ci struct acpi_iort_named_component *ncomp; 137462306a36Sopenharmony_ci 137562306a36Sopenharmony_ci node = iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT, 137662306a36Sopenharmony_ci iort_match_node_callback, dev); 137762306a36Sopenharmony_ci if (!node) 137862306a36Sopenharmony_ci return -ENODEV; 137962306a36Sopenharmony_ci 138062306a36Sopenharmony_ci ncomp = (struct acpi_iort_named_component *)node->node_data; 138162306a36Sopenharmony_ci 138262306a36Sopenharmony_ci if (!ncomp->memory_address_limit) { 138362306a36Sopenharmony_ci pr_warn(FW_BUG "Named component missing memory address limit\n"); 138462306a36Sopenharmony_ci return -EINVAL; 138562306a36Sopenharmony_ci } 138662306a36Sopenharmony_ci 138762306a36Sopenharmony_ci *size = ncomp->memory_address_limit >= 64 ? U64_MAX : 138862306a36Sopenharmony_ci 1ULL<<ncomp->memory_address_limit; 138962306a36Sopenharmony_ci 139062306a36Sopenharmony_ci return 0; 139162306a36Sopenharmony_ci} 139262306a36Sopenharmony_ci 139362306a36Sopenharmony_cistatic int rc_dma_get_range(struct device *dev, u64 *size) 139462306a36Sopenharmony_ci{ 139562306a36Sopenharmony_ci struct acpi_iort_node *node; 139662306a36Sopenharmony_ci struct acpi_iort_root_complex *rc; 139762306a36Sopenharmony_ci struct pci_bus *pbus = to_pci_dev(dev)->bus; 139862306a36Sopenharmony_ci 139962306a36Sopenharmony_ci node = iort_scan_node(ACPI_IORT_NODE_PCI_ROOT_COMPLEX, 140062306a36Sopenharmony_ci iort_match_node_callback, &pbus->dev); 140162306a36Sopenharmony_ci if (!node || node->revision < 1) 140262306a36Sopenharmony_ci return -ENODEV; 140362306a36Sopenharmony_ci 140462306a36Sopenharmony_ci rc = (struct acpi_iort_root_complex *)node->node_data; 140562306a36Sopenharmony_ci 140662306a36Sopenharmony_ci if (!rc->memory_address_limit) { 140762306a36Sopenharmony_ci pr_warn(FW_BUG "Root complex missing memory address limit\n"); 140862306a36Sopenharmony_ci return -EINVAL; 140962306a36Sopenharmony_ci } 141062306a36Sopenharmony_ci 141162306a36Sopenharmony_ci *size = rc->memory_address_limit >= 64 ? U64_MAX : 141262306a36Sopenharmony_ci 1ULL<<rc->memory_address_limit; 141362306a36Sopenharmony_ci 141462306a36Sopenharmony_ci return 0; 141562306a36Sopenharmony_ci} 141662306a36Sopenharmony_ci 141762306a36Sopenharmony_ci/** 141862306a36Sopenharmony_ci * iort_dma_get_ranges() - Look up DMA addressing limit for the device 141962306a36Sopenharmony_ci * @dev: device to lookup 142062306a36Sopenharmony_ci * @size: DMA range size result pointer 142162306a36Sopenharmony_ci * 142262306a36Sopenharmony_ci * Return: 0 on success, an error otherwise. 142362306a36Sopenharmony_ci */ 142462306a36Sopenharmony_ciint iort_dma_get_ranges(struct device *dev, u64 *size) 142562306a36Sopenharmony_ci{ 142662306a36Sopenharmony_ci if (dev_is_pci(dev)) 142762306a36Sopenharmony_ci return rc_dma_get_range(dev, size); 142862306a36Sopenharmony_ci else 142962306a36Sopenharmony_ci return nc_dma_get_range(dev, size); 143062306a36Sopenharmony_ci} 143162306a36Sopenharmony_ci 143262306a36Sopenharmony_cistatic void __init acpi_iort_register_irq(int hwirq, const char *name, 143362306a36Sopenharmony_ci int trigger, 143462306a36Sopenharmony_ci struct resource *res) 143562306a36Sopenharmony_ci{ 143662306a36Sopenharmony_ci int irq = acpi_register_gsi(NULL, hwirq, trigger, 143762306a36Sopenharmony_ci ACPI_ACTIVE_HIGH); 143862306a36Sopenharmony_ci 143962306a36Sopenharmony_ci if (irq <= 0) { 144062306a36Sopenharmony_ci pr_err("could not register gsi hwirq %d name [%s]\n", hwirq, 144162306a36Sopenharmony_ci name); 144262306a36Sopenharmony_ci return; 144362306a36Sopenharmony_ci } 144462306a36Sopenharmony_ci 144562306a36Sopenharmony_ci res->start = irq; 144662306a36Sopenharmony_ci res->end = irq; 144762306a36Sopenharmony_ci res->flags = IORESOURCE_IRQ; 144862306a36Sopenharmony_ci res->name = name; 144962306a36Sopenharmony_ci} 145062306a36Sopenharmony_ci 145162306a36Sopenharmony_cistatic int __init arm_smmu_v3_count_resources(struct acpi_iort_node *node) 145262306a36Sopenharmony_ci{ 145362306a36Sopenharmony_ci struct acpi_iort_smmu_v3 *smmu; 145462306a36Sopenharmony_ci /* Always present mem resource */ 145562306a36Sopenharmony_ci int num_res = 1; 145662306a36Sopenharmony_ci 145762306a36Sopenharmony_ci /* Retrieve SMMUv3 specific data */ 145862306a36Sopenharmony_ci smmu = (struct acpi_iort_smmu_v3 *)node->node_data; 145962306a36Sopenharmony_ci 146062306a36Sopenharmony_ci if (smmu->event_gsiv) 146162306a36Sopenharmony_ci num_res++; 146262306a36Sopenharmony_ci 146362306a36Sopenharmony_ci if (smmu->pri_gsiv) 146462306a36Sopenharmony_ci num_res++; 146562306a36Sopenharmony_ci 146662306a36Sopenharmony_ci if (smmu->gerr_gsiv) 146762306a36Sopenharmony_ci num_res++; 146862306a36Sopenharmony_ci 146962306a36Sopenharmony_ci if (smmu->sync_gsiv) 147062306a36Sopenharmony_ci num_res++; 147162306a36Sopenharmony_ci 147262306a36Sopenharmony_ci return num_res; 147362306a36Sopenharmony_ci} 147462306a36Sopenharmony_ci 147562306a36Sopenharmony_cistatic bool arm_smmu_v3_is_combined_irq(struct acpi_iort_smmu_v3 *smmu) 147662306a36Sopenharmony_ci{ 147762306a36Sopenharmony_ci /* 147862306a36Sopenharmony_ci * Cavium ThunderX2 implementation doesn't not support unique 147962306a36Sopenharmony_ci * irq line. Use single irq line for all the SMMUv3 interrupts. 148062306a36Sopenharmony_ci */ 148162306a36Sopenharmony_ci if (smmu->model != ACPI_IORT_SMMU_V3_CAVIUM_CN99XX) 148262306a36Sopenharmony_ci return false; 148362306a36Sopenharmony_ci 148462306a36Sopenharmony_ci /* 148562306a36Sopenharmony_ci * ThunderX2 doesn't support MSIs from the SMMU, so we're checking 148662306a36Sopenharmony_ci * SPI numbers here. 148762306a36Sopenharmony_ci */ 148862306a36Sopenharmony_ci return smmu->event_gsiv == smmu->pri_gsiv && 148962306a36Sopenharmony_ci smmu->event_gsiv == smmu->gerr_gsiv && 149062306a36Sopenharmony_ci smmu->event_gsiv == smmu->sync_gsiv; 149162306a36Sopenharmony_ci} 149262306a36Sopenharmony_ci 149362306a36Sopenharmony_cistatic unsigned long arm_smmu_v3_resource_size(struct acpi_iort_smmu_v3 *smmu) 149462306a36Sopenharmony_ci{ 149562306a36Sopenharmony_ci /* 149662306a36Sopenharmony_ci * Override the size, for Cavium ThunderX2 implementation 149762306a36Sopenharmony_ci * which doesn't support the page 1 SMMU register space. 149862306a36Sopenharmony_ci */ 149962306a36Sopenharmony_ci if (smmu->model == ACPI_IORT_SMMU_V3_CAVIUM_CN99XX) 150062306a36Sopenharmony_ci return SZ_64K; 150162306a36Sopenharmony_ci 150262306a36Sopenharmony_ci return SZ_128K; 150362306a36Sopenharmony_ci} 150462306a36Sopenharmony_ci 150562306a36Sopenharmony_cistatic void __init arm_smmu_v3_init_resources(struct resource *res, 150662306a36Sopenharmony_ci struct acpi_iort_node *node) 150762306a36Sopenharmony_ci{ 150862306a36Sopenharmony_ci struct acpi_iort_smmu_v3 *smmu; 150962306a36Sopenharmony_ci int num_res = 0; 151062306a36Sopenharmony_ci 151162306a36Sopenharmony_ci /* Retrieve SMMUv3 specific data */ 151262306a36Sopenharmony_ci smmu = (struct acpi_iort_smmu_v3 *)node->node_data; 151362306a36Sopenharmony_ci 151462306a36Sopenharmony_ci res[num_res].start = smmu->base_address; 151562306a36Sopenharmony_ci res[num_res].end = smmu->base_address + 151662306a36Sopenharmony_ci arm_smmu_v3_resource_size(smmu) - 1; 151762306a36Sopenharmony_ci res[num_res].flags = IORESOURCE_MEM; 151862306a36Sopenharmony_ci 151962306a36Sopenharmony_ci num_res++; 152062306a36Sopenharmony_ci if (arm_smmu_v3_is_combined_irq(smmu)) { 152162306a36Sopenharmony_ci if (smmu->event_gsiv) 152262306a36Sopenharmony_ci acpi_iort_register_irq(smmu->event_gsiv, "combined", 152362306a36Sopenharmony_ci ACPI_EDGE_SENSITIVE, 152462306a36Sopenharmony_ci &res[num_res++]); 152562306a36Sopenharmony_ci } else { 152662306a36Sopenharmony_ci 152762306a36Sopenharmony_ci if (smmu->event_gsiv) 152862306a36Sopenharmony_ci acpi_iort_register_irq(smmu->event_gsiv, "eventq", 152962306a36Sopenharmony_ci ACPI_EDGE_SENSITIVE, 153062306a36Sopenharmony_ci &res[num_res++]); 153162306a36Sopenharmony_ci 153262306a36Sopenharmony_ci if (smmu->pri_gsiv) 153362306a36Sopenharmony_ci acpi_iort_register_irq(smmu->pri_gsiv, "priq", 153462306a36Sopenharmony_ci ACPI_EDGE_SENSITIVE, 153562306a36Sopenharmony_ci &res[num_res++]); 153662306a36Sopenharmony_ci 153762306a36Sopenharmony_ci if (smmu->gerr_gsiv) 153862306a36Sopenharmony_ci acpi_iort_register_irq(smmu->gerr_gsiv, "gerror", 153962306a36Sopenharmony_ci ACPI_EDGE_SENSITIVE, 154062306a36Sopenharmony_ci &res[num_res++]); 154162306a36Sopenharmony_ci 154262306a36Sopenharmony_ci if (smmu->sync_gsiv) 154362306a36Sopenharmony_ci acpi_iort_register_irq(smmu->sync_gsiv, "cmdq-sync", 154462306a36Sopenharmony_ci ACPI_EDGE_SENSITIVE, 154562306a36Sopenharmony_ci &res[num_res++]); 154662306a36Sopenharmony_ci } 154762306a36Sopenharmony_ci} 154862306a36Sopenharmony_ci 154962306a36Sopenharmony_cistatic void __init arm_smmu_v3_dma_configure(struct device *dev, 155062306a36Sopenharmony_ci struct acpi_iort_node *node) 155162306a36Sopenharmony_ci{ 155262306a36Sopenharmony_ci struct acpi_iort_smmu_v3 *smmu; 155362306a36Sopenharmony_ci enum dev_dma_attr attr; 155462306a36Sopenharmony_ci 155562306a36Sopenharmony_ci /* Retrieve SMMUv3 specific data */ 155662306a36Sopenharmony_ci smmu = (struct acpi_iort_smmu_v3 *)node->node_data; 155762306a36Sopenharmony_ci 155862306a36Sopenharmony_ci attr = (smmu->flags & ACPI_IORT_SMMU_V3_COHACC_OVERRIDE) ? 155962306a36Sopenharmony_ci DEV_DMA_COHERENT : DEV_DMA_NON_COHERENT; 156062306a36Sopenharmony_ci 156162306a36Sopenharmony_ci /* We expect the dma masks to be equivalent for all SMMUv3 set-ups */ 156262306a36Sopenharmony_ci dev->dma_mask = &dev->coherent_dma_mask; 156362306a36Sopenharmony_ci 156462306a36Sopenharmony_ci /* Configure DMA for the page table walker */ 156562306a36Sopenharmony_ci acpi_dma_configure(dev, attr); 156662306a36Sopenharmony_ci} 156762306a36Sopenharmony_ci 156862306a36Sopenharmony_ci#if defined(CONFIG_ACPI_NUMA) 156962306a36Sopenharmony_ci/* 157062306a36Sopenharmony_ci * set numa proximity domain for smmuv3 device 157162306a36Sopenharmony_ci */ 157262306a36Sopenharmony_cistatic int __init arm_smmu_v3_set_proximity(struct device *dev, 157362306a36Sopenharmony_ci struct acpi_iort_node *node) 157462306a36Sopenharmony_ci{ 157562306a36Sopenharmony_ci struct acpi_iort_smmu_v3 *smmu; 157662306a36Sopenharmony_ci 157762306a36Sopenharmony_ci smmu = (struct acpi_iort_smmu_v3 *)node->node_data; 157862306a36Sopenharmony_ci if (smmu->flags & ACPI_IORT_SMMU_V3_PXM_VALID) { 157962306a36Sopenharmony_ci int dev_node = pxm_to_node(smmu->pxm); 158062306a36Sopenharmony_ci 158162306a36Sopenharmony_ci if (dev_node != NUMA_NO_NODE && !node_online(dev_node)) 158262306a36Sopenharmony_ci return -EINVAL; 158362306a36Sopenharmony_ci 158462306a36Sopenharmony_ci set_dev_node(dev, dev_node); 158562306a36Sopenharmony_ci pr_info("SMMU-v3[%llx] Mapped to Proximity domain %d\n", 158662306a36Sopenharmony_ci smmu->base_address, 158762306a36Sopenharmony_ci smmu->pxm); 158862306a36Sopenharmony_ci } 158962306a36Sopenharmony_ci return 0; 159062306a36Sopenharmony_ci} 159162306a36Sopenharmony_ci#else 159262306a36Sopenharmony_ci#define arm_smmu_v3_set_proximity NULL 159362306a36Sopenharmony_ci#endif 159462306a36Sopenharmony_ci 159562306a36Sopenharmony_cistatic int __init arm_smmu_count_resources(struct acpi_iort_node *node) 159662306a36Sopenharmony_ci{ 159762306a36Sopenharmony_ci struct acpi_iort_smmu *smmu; 159862306a36Sopenharmony_ci 159962306a36Sopenharmony_ci /* Retrieve SMMU specific data */ 160062306a36Sopenharmony_ci smmu = (struct acpi_iort_smmu *)node->node_data; 160162306a36Sopenharmony_ci 160262306a36Sopenharmony_ci /* 160362306a36Sopenharmony_ci * Only consider the global fault interrupt and ignore the 160462306a36Sopenharmony_ci * configuration access interrupt. 160562306a36Sopenharmony_ci * 160662306a36Sopenharmony_ci * MMIO address and global fault interrupt resources are always 160762306a36Sopenharmony_ci * present so add them to the context interrupt count as a static 160862306a36Sopenharmony_ci * value. 160962306a36Sopenharmony_ci */ 161062306a36Sopenharmony_ci return smmu->context_interrupt_count + 2; 161162306a36Sopenharmony_ci} 161262306a36Sopenharmony_ci 161362306a36Sopenharmony_cistatic void __init arm_smmu_init_resources(struct resource *res, 161462306a36Sopenharmony_ci struct acpi_iort_node *node) 161562306a36Sopenharmony_ci{ 161662306a36Sopenharmony_ci struct acpi_iort_smmu *smmu; 161762306a36Sopenharmony_ci int i, hw_irq, trigger, num_res = 0; 161862306a36Sopenharmony_ci u64 *ctx_irq, *glb_irq; 161962306a36Sopenharmony_ci 162062306a36Sopenharmony_ci /* Retrieve SMMU specific data */ 162162306a36Sopenharmony_ci smmu = (struct acpi_iort_smmu *)node->node_data; 162262306a36Sopenharmony_ci 162362306a36Sopenharmony_ci res[num_res].start = smmu->base_address; 162462306a36Sopenharmony_ci res[num_res].end = smmu->base_address + smmu->span - 1; 162562306a36Sopenharmony_ci res[num_res].flags = IORESOURCE_MEM; 162662306a36Sopenharmony_ci num_res++; 162762306a36Sopenharmony_ci 162862306a36Sopenharmony_ci glb_irq = ACPI_ADD_PTR(u64, node, smmu->global_interrupt_offset); 162962306a36Sopenharmony_ci /* Global IRQs */ 163062306a36Sopenharmony_ci hw_irq = IORT_IRQ_MASK(glb_irq[0]); 163162306a36Sopenharmony_ci trigger = IORT_IRQ_TRIGGER_MASK(glb_irq[0]); 163262306a36Sopenharmony_ci 163362306a36Sopenharmony_ci acpi_iort_register_irq(hw_irq, "arm-smmu-global", trigger, 163462306a36Sopenharmony_ci &res[num_res++]); 163562306a36Sopenharmony_ci 163662306a36Sopenharmony_ci /* Context IRQs */ 163762306a36Sopenharmony_ci ctx_irq = ACPI_ADD_PTR(u64, node, smmu->context_interrupt_offset); 163862306a36Sopenharmony_ci for (i = 0; i < smmu->context_interrupt_count; i++) { 163962306a36Sopenharmony_ci hw_irq = IORT_IRQ_MASK(ctx_irq[i]); 164062306a36Sopenharmony_ci trigger = IORT_IRQ_TRIGGER_MASK(ctx_irq[i]); 164162306a36Sopenharmony_ci 164262306a36Sopenharmony_ci acpi_iort_register_irq(hw_irq, "arm-smmu-context", trigger, 164362306a36Sopenharmony_ci &res[num_res++]); 164462306a36Sopenharmony_ci } 164562306a36Sopenharmony_ci} 164662306a36Sopenharmony_ci 164762306a36Sopenharmony_cistatic void __init arm_smmu_dma_configure(struct device *dev, 164862306a36Sopenharmony_ci struct acpi_iort_node *node) 164962306a36Sopenharmony_ci{ 165062306a36Sopenharmony_ci struct acpi_iort_smmu *smmu; 165162306a36Sopenharmony_ci enum dev_dma_attr attr; 165262306a36Sopenharmony_ci 165362306a36Sopenharmony_ci /* Retrieve SMMU specific data */ 165462306a36Sopenharmony_ci smmu = (struct acpi_iort_smmu *)node->node_data; 165562306a36Sopenharmony_ci 165662306a36Sopenharmony_ci attr = (smmu->flags & ACPI_IORT_SMMU_COHERENT_WALK) ? 165762306a36Sopenharmony_ci DEV_DMA_COHERENT : DEV_DMA_NON_COHERENT; 165862306a36Sopenharmony_ci 165962306a36Sopenharmony_ci /* We expect the dma masks to be equivalent for SMMU set-ups */ 166062306a36Sopenharmony_ci dev->dma_mask = &dev->coherent_dma_mask; 166162306a36Sopenharmony_ci 166262306a36Sopenharmony_ci /* Configure DMA for the page table walker */ 166362306a36Sopenharmony_ci acpi_dma_configure(dev, attr); 166462306a36Sopenharmony_ci} 166562306a36Sopenharmony_ci 166662306a36Sopenharmony_cistatic int __init arm_smmu_v3_pmcg_count_resources(struct acpi_iort_node *node) 166762306a36Sopenharmony_ci{ 166862306a36Sopenharmony_ci struct acpi_iort_pmcg *pmcg; 166962306a36Sopenharmony_ci 167062306a36Sopenharmony_ci /* Retrieve PMCG specific data */ 167162306a36Sopenharmony_ci pmcg = (struct acpi_iort_pmcg *)node->node_data; 167262306a36Sopenharmony_ci 167362306a36Sopenharmony_ci /* 167462306a36Sopenharmony_ci * There are always 2 memory resources. 167562306a36Sopenharmony_ci * If the overflow_gsiv is present then add that for a total of 3. 167662306a36Sopenharmony_ci */ 167762306a36Sopenharmony_ci return pmcg->overflow_gsiv ? 3 : 2; 167862306a36Sopenharmony_ci} 167962306a36Sopenharmony_ci 168062306a36Sopenharmony_cistatic void __init arm_smmu_v3_pmcg_init_resources(struct resource *res, 168162306a36Sopenharmony_ci struct acpi_iort_node *node) 168262306a36Sopenharmony_ci{ 168362306a36Sopenharmony_ci struct acpi_iort_pmcg *pmcg; 168462306a36Sopenharmony_ci 168562306a36Sopenharmony_ci /* Retrieve PMCG specific data */ 168662306a36Sopenharmony_ci pmcg = (struct acpi_iort_pmcg *)node->node_data; 168762306a36Sopenharmony_ci 168862306a36Sopenharmony_ci res[0].start = pmcg->page0_base_address; 168962306a36Sopenharmony_ci res[0].end = pmcg->page0_base_address + SZ_4K - 1; 169062306a36Sopenharmony_ci res[0].flags = IORESOURCE_MEM; 169162306a36Sopenharmony_ci /* 169262306a36Sopenharmony_ci * The initial version in DEN0049C lacked a way to describe register 169362306a36Sopenharmony_ci * page 1, which makes it broken for most PMCG implementations; in 169462306a36Sopenharmony_ci * that case, just let the driver fail gracefully if it expects to 169562306a36Sopenharmony_ci * find a second memory resource. 169662306a36Sopenharmony_ci */ 169762306a36Sopenharmony_ci if (node->revision > 0) { 169862306a36Sopenharmony_ci res[1].start = pmcg->page1_base_address; 169962306a36Sopenharmony_ci res[1].end = pmcg->page1_base_address + SZ_4K - 1; 170062306a36Sopenharmony_ci res[1].flags = IORESOURCE_MEM; 170162306a36Sopenharmony_ci } 170262306a36Sopenharmony_ci 170362306a36Sopenharmony_ci if (pmcg->overflow_gsiv) 170462306a36Sopenharmony_ci acpi_iort_register_irq(pmcg->overflow_gsiv, "overflow", 170562306a36Sopenharmony_ci ACPI_EDGE_SENSITIVE, &res[2]); 170662306a36Sopenharmony_ci} 170762306a36Sopenharmony_ci 170862306a36Sopenharmony_cistatic struct acpi_platform_list pmcg_plat_info[] __initdata = { 170962306a36Sopenharmony_ci /* HiSilicon Hip08 Platform */ 171062306a36Sopenharmony_ci {"HISI ", "HIP08 ", 0, ACPI_SIG_IORT, greater_than_or_equal, 171162306a36Sopenharmony_ci "Erratum #162001800, Erratum #162001900", IORT_SMMU_V3_PMCG_HISI_HIP08}, 171262306a36Sopenharmony_ci /* HiSilicon Hip09 Platform */ 171362306a36Sopenharmony_ci {"HISI ", "HIP09 ", 0, ACPI_SIG_IORT, greater_than_or_equal, 171462306a36Sopenharmony_ci "Erratum #162001900", IORT_SMMU_V3_PMCG_HISI_HIP09}, 171562306a36Sopenharmony_ci { } 171662306a36Sopenharmony_ci}; 171762306a36Sopenharmony_ci 171862306a36Sopenharmony_cistatic int __init arm_smmu_v3_pmcg_add_platdata(struct platform_device *pdev) 171962306a36Sopenharmony_ci{ 172062306a36Sopenharmony_ci u32 model; 172162306a36Sopenharmony_ci int idx; 172262306a36Sopenharmony_ci 172362306a36Sopenharmony_ci idx = acpi_match_platform_list(pmcg_plat_info); 172462306a36Sopenharmony_ci if (idx >= 0) 172562306a36Sopenharmony_ci model = pmcg_plat_info[idx].data; 172662306a36Sopenharmony_ci else 172762306a36Sopenharmony_ci model = IORT_SMMU_V3_PMCG_GENERIC; 172862306a36Sopenharmony_ci 172962306a36Sopenharmony_ci return platform_device_add_data(pdev, &model, sizeof(model)); 173062306a36Sopenharmony_ci} 173162306a36Sopenharmony_ci 173262306a36Sopenharmony_cistruct iort_dev_config { 173362306a36Sopenharmony_ci const char *name; 173462306a36Sopenharmony_ci int (*dev_init)(struct acpi_iort_node *node); 173562306a36Sopenharmony_ci void (*dev_dma_configure)(struct device *dev, 173662306a36Sopenharmony_ci struct acpi_iort_node *node); 173762306a36Sopenharmony_ci int (*dev_count_resources)(struct acpi_iort_node *node); 173862306a36Sopenharmony_ci void (*dev_init_resources)(struct resource *res, 173962306a36Sopenharmony_ci struct acpi_iort_node *node); 174062306a36Sopenharmony_ci int (*dev_set_proximity)(struct device *dev, 174162306a36Sopenharmony_ci struct acpi_iort_node *node); 174262306a36Sopenharmony_ci int (*dev_add_platdata)(struct platform_device *pdev); 174362306a36Sopenharmony_ci}; 174462306a36Sopenharmony_ci 174562306a36Sopenharmony_cistatic const struct iort_dev_config iort_arm_smmu_v3_cfg __initconst = { 174662306a36Sopenharmony_ci .name = "arm-smmu-v3", 174762306a36Sopenharmony_ci .dev_dma_configure = arm_smmu_v3_dma_configure, 174862306a36Sopenharmony_ci .dev_count_resources = arm_smmu_v3_count_resources, 174962306a36Sopenharmony_ci .dev_init_resources = arm_smmu_v3_init_resources, 175062306a36Sopenharmony_ci .dev_set_proximity = arm_smmu_v3_set_proximity, 175162306a36Sopenharmony_ci}; 175262306a36Sopenharmony_ci 175362306a36Sopenharmony_cistatic const struct iort_dev_config iort_arm_smmu_cfg __initconst = { 175462306a36Sopenharmony_ci .name = "arm-smmu", 175562306a36Sopenharmony_ci .dev_dma_configure = arm_smmu_dma_configure, 175662306a36Sopenharmony_ci .dev_count_resources = arm_smmu_count_resources, 175762306a36Sopenharmony_ci .dev_init_resources = arm_smmu_init_resources, 175862306a36Sopenharmony_ci}; 175962306a36Sopenharmony_ci 176062306a36Sopenharmony_cistatic const struct iort_dev_config iort_arm_smmu_v3_pmcg_cfg __initconst = { 176162306a36Sopenharmony_ci .name = "arm-smmu-v3-pmcg", 176262306a36Sopenharmony_ci .dev_count_resources = arm_smmu_v3_pmcg_count_resources, 176362306a36Sopenharmony_ci .dev_init_resources = arm_smmu_v3_pmcg_init_resources, 176462306a36Sopenharmony_ci .dev_add_platdata = arm_smmu_v3_pmcg_add_platdata, 176562306a36Sopenharmony_ci}; 176662306a36Sopenharmony_ci 176762306a36Sopenharmony_cistatic __init const struct iort_dev_config *iort_get_dev_cfg( 176862306a36Sopenharmony_ci struct acpi_iort_node *node) 176962306a36Sopenharmony_ci{ 177062306a36Sopenharmony_ci switch (node->type) { 177162306a36Sopenharmony_ci case ACPI_IORT_NODE_SMMU_V3: 177262306a36Sopenharmony_ci return &iort_arm_smmu_v3_cfg; 177362306a36Sopenharmony_ci case ACPI_IORT_NODE_SMMU: 177462306a36Sopenharmony_ci return &iort_arm_smmu_cfg; 177562306a36Sopenharmony_ci case ACPI_IORT_NODE_PMCG: 177662306a36Sopenharmony_ci return &iort_arm_smmu_v3_pmcg_cfg; 177762306a36Sopenharmony_ci default: 177862306a36Sopenharmony_ci return NULL; 177962306a36Sopenharmony_ci } 178062306a36Sopenharmony_ci} 178162306a36Sopenharmony_ci 178262306a36Sopenharmony_ci/** 178362306a36Sopenharmony_ci * iort_add_platform_device() - Allocate a platform device for IORT node 178462306a36Sopenharmony_ci * @node: Pointer to device ACPI IORT node 178562306a36Sopenharmony_ci * @ops: Pointer to IORT device config struct 178662306a36Sopenharmony_ci * 178762306a36Sopenharmony_ci * Returns: 0 on success, <0 failure 178862306a36Sopenharmony_ci */ 178962306a36Sopenharmony_cistatic int __init iort_add_platform_device(struct acpi_iort_node *node, 179062306a36Sopenharmony_ci const struct iort_dev_config *ops) 179162306a36Sopenharmony_ci{ 179262306a36Sopenharmony_ci struct fwnode_handle *fwnode; 179362306a36Sopenharmony_ci struct platform_device *pdev; 179462306a36Sopenharmony_ci struct resource *r; 179562306a36Sopenharmony_ci int ret, count; 179662306a36Sopenharmony_ci 179762306a36Sopenharmony_ci pdev = platform_device_alloc(ops->name, PLATFORM_DEVID_AUTO); 179862306a36Sopenharmony_ci if (!pdev) 179962306a36Sopenharmony_ci return -ENOMEM; 180062306a36Sopenharmony_ci 180162306a36Sopenharmony_ci if (ops->dev_set_proximity) { 180262306a36Sopenharmony_ci ret = ops->dev_set_proximity(&pdev->dev, node); 180362306a36Sopenharmony_ci if (ret) 180462306a36Sopenharmony_ci goto dev_put; 180562306a36Sopenharmony_ci } 180662306a36Sopenharmony_ci 180762306a36Sopenharmony_ci count = ops->dev_count_resources(node); 180862306a36Sopenharmony_ci 180962306a36Sopenharmony_ci r = kcalloc(count, sizeof(*r), GFP_KERNEL); 181062306a36Sopenharmony_ci if (!r) { 181162306a36Sopenharmony_ci ret = -ENOMEM; 181262306a36Sopenharmony_ci goto dev_put; 181362306a36Sopenharmony_ci } 181462306a36Sopenharmony_ci 181562306a36Sopenharmony_ci ops->dev_init_resources(r, node); 181662306a36Sopenharmony_ci 181762306a36Sopenharmony_ci ret = platform_device_add_resources(pdev, r, count); 181862306a36Sopenharmony_ci /* 181962306a36Sopenharmony_ci * Resources are duplicated in platform_device_add_resources, 182062306a36Sopenharmony_ci * free their allocated memory 182162306a36Sopenharmony_ci */ 182262306a36Sopenharmony_ci kfree(r); 182362306a36Sopenharmony_ci 182462306a36Sopenharmony_ci if (ret) 182562306a36Sopenharmony_ci goto dev_put; 182662306a36Sopenharmony_ci 182762306a36Sopenharmony_ci /* 182862306a36Sopenharmony_ci * Platform devices based on PMCG nodes uses platform_data to 182962306a36Sopenharmony_ci * pass the hardware model info to the driver. For others, add 183062306a36Sopenharmony_ci * a copy of IORT node pointer to platform_data to be used to 183162306a36Sopenharmony_ci * retrieve IORT data information. 183262306a36Sopenharmony_ci */ 183362306a36Sopenharmony_ci if (ops->dev_add_platdata) 183462306a36Sopenharmony_ci ret = ops->dev_add_platdata(pdev); 183562306a36Sopenharmony_ci else 183662306a36Sopenharmony_ci ret = platform_device_add_data(pdev, &node, sizeof(node)); 183762306a36Sopenharmony_ci 183862306a36Sopenharmony_ci if (ret) 183962306a36Sopenharmony_ci goto dev_put; 184062306a36Sopenharmony_ci 184162306a36Sopenharmony_ci fwnode = iort_get_fwnode(node); 184262306a36Sopenharmony_ci 184362306a36Sopenharmony_ci if (!fwnode) { 184462306a36Sopenharmony_ci ret = -ENODEV; 184562306a36Sopenharmony_ci goto dev_put; 184662306a36Sopenharmony_ci } 184762306a36Sopenharmony_ci 184862306a36Sopenharmony_ci pdev->dev.fwnode = fwnode; 184962306a36Sopenharmony_ci 185062306a36Sopenharmony_ci if (ops->dev_dma_configure) 185162306a36Sopenharmony_ci ops->dev_dma_configure(&pdev->dev, node); 185262306a36Sopenharmony_ci 185362306a36Sopenharmony_ci iort_set_device_domain(&pdev->dev, node); 185462306a36Sopenharmony_ci 185562306a36Sopenharmony_ci ret = platform_device_add(pdev); 185662306a36Sopenharmony_ci if (ret) 185762306a36Sopenharmony_ci goto dma_deconfigure; 185862306a36Sopenharmony_ci 185962306a36Sopenharmony_ci return 0; 186062306a36Sopenharmony_ci 186162306a36Sopenharmony_cidma_deconfigure: 186262306a36Sopenharmony_ci arch_teardown_dma_ops(&pdev->dev); 186362306a36Sopenharmony_cidev_put: 186462306a36Sopenharmony_ci platform_device_put(pdev); 186562306a36Sopenharmony_ci 186662306a36Sopenharmony_ci return ret; 186762306a36Sopenharmony_ci} 186862306a36Sopenharmony_ci 186962306a36Sopenharmony_ci#ifdef CONFIG_PCI 187062306a36Sopenharmony_cistatic void __init iort_enable_acs(struct acpi_iort_node *iort_node) 187162306a36Sopenharmony_ci{ 187262306a36Sopenharmony_ci static bool acs_enabled __initdata; 187362306a36Sopenharmony_ci 187462306a36Sopenharmony_ci if (acs_enabled) 187562306a36Sopenharmony_ci return; 187662306a36Sopenharmony_ci 187762306a36Sopenharmony_ci if (iort_node->type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX) { 187862306a36Sopenharmony_ci struct acpi_iort_node *parent; 187962306a36Sopenharmony_ci struct acpi_iort_id_mapping *map; 188062306a36Sopenharmony_ci int i; 188162306a36Sopenharmony_ci 188262306a36Sopenharmony_ci map = ACPI_ADD_PTR(struct acpi_iort_id_mapping, iort_node, 188362306a36Sopenharmony_ci iort_node->mapping_offset); 188462306a36Sopenharmony_ci 188562306a36Sopenharmony_ci for (i = 0; i < iort_node->mapping_count; i++, map++) { 188662306a36Sopenharmony_ci if (!map->output_reference) 188762306a36Sopenharmony_ci continue; 188862306a36Sopenharmony_ci 188962306a36Sopenharmony_ci parent = ACPI_ADD_PTR(struct acpi_iort_node, 189062306a36Sopenharmony_ci iort_table, map->output_reference); 189162306a36Sopenharmony_ci /* 189262306a36Sopenharmony_ci * If we detect a RC->SMMU mapping, make sure 189362306a36Sopenharmony_ci * we enable ACS on the system. 189462306a36Sopenharmony_ci */ 189562306a36Sopenharmony_ci if ((parent->type == ACPI_IORT_NODE_SMMU) || 189662306a36Sopenharmony_ci (parent->type == ACPI_IORT_NODE_SMMU_V3)) { 189762306a36Sopenharmony_ci pci_request_acs(); 189862306a36Sopenharmony_ci acs_enabled = true; 189962306a36Sopenharmony_ci return; 190062306a36Sopenharmony_ci } 190162306a36Sopenharmony_ci } 190262306a36Sopenharmony_ci } 190362306a36Sopenharmony_ci} 190462306a36Sopenharmony_ci#else 190562306a36Sopenharmony_cistatic inline void iort_enable_acs(struct acpi_iort_node *iort_node) { } 190662306a36Sopenharmony_ci#endif 190762306a36Sopenharmony_ci 190862306a36Sopenharmony_cistatic void __init iort_init_platform_devices(void) 190962306a36Sopenharmony_ci{ 191062306a36Sopenharmony_ci struct acpi_iort_node *iort_node, *iort_end; 191162306a36Sopenharmony_ci struct acpi_table_iort *iort; 191262306a36Sopenharmony_ci struct fwnode_handle *fwnode; 191362306a36Sopenharmony_ci int i, ret; 191462306a36Sopenharmony_ci const struct iort_dev_config *ops; 191562306a36Sopenharmony_ci 191662306a36Sopenharmony_ci /* 191762306a36Sopenharmony_ci * iort_table and iort both point to the start of IORT table, but 191862306a36Sopenharmony_ci * have different struct types 191962306a36Sopenharmony_ci */ 192062306a36Sopenharmony_ci iort = (struct acpi_table_iort *)iort_table; 192162306a36Sopenharmony_ci 192262306a36Sopenharmony_ci /* Get the first IORT node */ 192362306a36Sopenharmony_ci iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort, 192462306a36Sopenharmony_ci iort->node_offset); 192562306a36Sopenharmony_ci iort_end = ACPI_ADD_PTR(struct acpi_iort_node, iort, 192662306a36Sopenharmony_ci iort_table->length); 192762306a36Sopenharmony_ci 192862306a36Sopenharmony_ci for (i = 0; i < iort->node_count; i++) { 192962306a36Sopenharmony_ci if (iort_node >= iort_end) { 193062306a36Sopenharmony_ci pr_err("iort node pointer overflows, bad table\n"); 193162306a36Sopenharmony_ci return; 193262306a36Sopenharmony_ci } 193362306a36Sopenharmony_ci 193462306a36Sopenharmony_ci iort_enable_acs(iort_node); 193562306a36Sopenharmony_ci 193662306a36Sopenharmony_ci ops = iort_get_dev_cfg(iort_node); 193762306a36Sopenharmony_ci if (ops) { 193862306a36Sopenharmony_ci fwnode = acpi_alloc_fwnode_static(); 193962306a36Sopenharmony_ci if (!fwnode) 194062306a36Sopenharmony_ci return; 194162306a36Sopenharmony_ci 194262306a36Sopenharmony_ci iort_set_fwnode(iort_node, fwnode); 194362306a36Sopenharmony_ci 194462306a36Sopenharmony_ci ret = iort_add_platform_device(iort_node, ops); 194562306a36Sopenharmony_ci if (ret) { 194662306a36Sopenharmony_ci iort_delete_fwnode(iort_node); 194762306a36Sopenharmony_ci acpi_free_fwnode_static(fwnode); 194862306a36Sopenharmony_ci return; 194962306a36Sopenharmony_ci } 195062306a36Sopenharmony_ci } 195162306a36Sopenharmony_ci 195262306a36Sopenharmony_ci iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort_node, 195362306a36Sopenharmony_ci iort_node->length); 195462306a36Sopenharmony_ci } 195562306a36Sopenharmony_ci} 195662306a36Sopenharmony_ci 195762306a36Sopenharmony_civoid __init acpi_iort_init(void) 195862306a36Sopenharmony_ci{ 195962306a36Sopenharmony_ci acpi_status status; 196062306a36Sopenharmony_ci 196162306a36Sopenharmony_ci /* iort_table will be used at runtime after the iort init, 196262306a36Sopenharmony_ci * so we don't need to call acpi_put_table() to release 196362306a36Sopenharmony_ci * the IORT table mapping. 196462306a36Sopenharmony_ci */ 196562306a36Sopenharmony_ci status = acpi_get_table(ACPI_SIG_IORT, 0, &iort_table); 196662306a36Sopenharmony_ci if (ACPI_FAILURE(status)) { 196762306a36Sopenharmony_ci if (status != AE_NOT_FOUND) { 196862306a36Sopenharmony_ci const char *msg = acpi_format_exception(status); 196962306a36Sopenharmony_ci 197062306a36Sopenharmony_ci pr_err("Failed to get table, %s\n", msg); 197162306a36Sopenharmony_ci } 197262306a36Sopenharmony_ci 197362306a36Sopenharmony_ci return; 197462306a36Sopenharmony_ci } 197562306a36Sopenharmony_ci 197662306a36Sopenharmony_ci iort_init_platform_devices(); 197762306a36Sopenharmony_ci} 197862306a36Sopenharmony_ci 197962306a36Sopenharmony_ci#ifdef CONFIG_ZONE_DMA 198062306a36Sopenharmony_ci/* 198162306a36Sopenharmony_ci * Extract the highest CPU physical address accessible to all DMA masters in 198262306a36Sopenharmony_ci * the system. PHYS_ADDR_MAX is returned when no constrained device is found. 198362306a36Sopenharmony_ci */ 198462306a36Sopenharmony_ciphys_addr_t __init acpi_iort_dma_get_max_cpu_address(void) 198562306a36Sopenharmony_ci{ 198662306a36Sopenharmony_ci phys_addr_t limit = PHYS_ADDR_MAX; 198762306a36Sopenharmony_ci struct acpi_iort_node *node, *end; 198862306a36Sopenharmony_ci struct acpi_table_iort *iort; 198962306a36Sopenharmony_ci acpi_status status; 199062306a36Sopenharmony_ci int i; 199162306a36Sopenharmony_ci 199262306a36Sopenharmony_ci if (acpi_disabled) 199362306a36Sopenharmony_ci return limit; 199462306a36Sopenharmony_ci 199562306a36Sopenharmony_ci status = acpi_get_table(ACPI_SIG_IORT, 0, 199662306a36Sopenharmony_ci (struct acpi_table_header **)&iort); 199762306a36Sopenharmony_ci if (ACPI_FAILURE(status)) 199862306a36Sopenharmony_ci return limit; 199962306a36Sopenharmony_ci 200062306a36Sopenharmony_ci node = ACPI_ADD_PTR(struct acpi_iort_node, iort, iort->node_offset); 200162306a36Sopenharmony_ci end = ACPI_ADD_PTR(struct acpi_iort_node, iort, iort->header.length); 200262306a36Sopenharmony_ci 200362306a36Sopenharmony_ci for (i = 0; i < iort->node_count; i++) { 200462306a36Sopenharmony_ci if (node >= end) 200562306a36Sopenharmony_ci break; 200662306a36Sopenharmony_ci 200762306a36Sopenharmony_ci switch (node->type) { 200862306a36Sopenharmony_ci struct acpi_iort_named_component *ncomp; 200962306a36Sopenharmony_ci struct acpi_iort_root_complex *rc; 201062306a36Sopenharmony_ci phys_addr_t local_limit; 201162306a36Sopenharmony_ci 201262306a36Sopenharmony_ci case ACPI_IORT_NODE_NAMED_COMPONENT: 201362306a36Sopenharmony_ci ncomp = (struct acpi_iort_named_component *)node->node_data; 201462306a36Sopenharmony_ci local_limit = DMA_BIT_MASK(ncomp->memory_address_limit); 201562306a36Sopenharmony_ci limit = min_not_zero(limit, local_limit); 201662306a36Sopenharmony_ci break; 201762306a36Sopenharmony_ci 201862306a36Sopenharmony_ci case ACPI_IORT_NODE_PCI_ROOT_COMPLEX: 201962306a36Sopenharmony_ci if (node->revision < 1) 202062306a36Sopenharmony_ci break; 202162306a36Sopenharmony_ci 202262306a36Sopenharmony_ci rc = (struct acpi_iort_root_complex *)node->node_data; 202362306a36Sopenharmony_ci local_limit = DMA_BIT_MASK(rc->memory_address_limit); 202462306a36Sopenharmony_ci limit = min_not_zero(limit, local_limit); 202562306a36Sopenharmony_ci break; 202662306a36Sopenharmony_ci } 202762306a36Sopenharmony_ci node = ACPI_ADD_PTR(struct acpi_iort_node, node, node->length); 202862306a36Sopenharmony_ci } 202962306a36Sopenharmony_ci acpi_put_table(&iort->header); 203062306a36Sopenharmony_ci return limit; 203162306a36Sopenharmony_ci} 203262306a36Sopenharmony_ci#endif 2033