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