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(&region->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(&region->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