Commit 04cd14ff authored by Rafael J. Wysocki's avatar Rafael J. Wysocki
Browse files

Merge branch 'acpi-irq'

Merge ARM-related irq subsystem changes based on the recent ACPICA
updates for 6.20-rc1/7.0-rc1:

 - Add support for GICv5 ACPI probing on ARM which is based on the
   GICv5 MADT structures and ARM IORT IWB node definitions recently
   added to ACPICA (Lorenzo Pieralisi)

* acpi-irq:
  irqchip/gic-v5: Add ACPI IWB probing
  irqchip/gic-v5: Add ACPI ITS probing
  irqchip/gic-v5: Add ACPI IRS probing
  irqchip/gic-v5: Split IRS probing into OF and generic portions
  PCI/MSI: Make the pci_msi_map_rid_ctlr_node() interface firmware agnostic
  irqdomain: Add parent field to struct irqchip_fwid
parents 0e8ac1d3 05bff341
Loading
Loading
Loading
Loading
+157 −36
Original line number Diff line number Diff line
@@ -264,39 +264,47 @@ static acpi_status iort_match_node_callback(struct acpi_iort_node *node,
	struct device *dev = context;
	acpi_status status = AE_NOT_FOUND;

	if (node->type == ACPI_IORT_NODE_NAMED_COMPONENT) {
	if (node->type == ACPI_IORT_NODE_NAMED_COMPONENT ||
	    node->type == ACPI_IORT_NODE_IWB) {
		struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
		struct acpi_device *adev;
		struct acpi_iort_named_component *ncomp;
		struct device *nc_dev = dev;
		struct acpi_iort_iwb *iwb;
		struct device *cdev = dev;
		struct acpi_device *adev;
		const char *device_name;

		/*
		 * Walk the device tree to find a device with an
		 * ACPI companion; there is no point in scanning
		 * IORT for a device matching a named component if
		 * IORT for a device matching a named component or IWB if
		 * the device does not have an ACPI companion to
		 * start with.
		 */
		do {
			adev = ACPI_COMPANION(nc_dev);
			adev = ACPI_COMPANION(cdev);
			if (adev)
				break;

			nc_dev = nc_dev->parent;
		} while (nc_dev);
			cdev = cdev->parent;
		} while (cdev);

		if (!adev)
			goto out;

		status = acpi_get_name(adev->handle, ACPI_FULL_PATHNAME, &buf);
		if (ACPI_FAILURE(status)) {
			dev_warn(nc_dev, "Can't get device full path name\n");
			dev_warn(cdev, "Can't get device full path name\n");
			goto out;
		}

		if (node->type == ACPI_IORT_NODE_NAMED_COMPONENT) {
			ncomp = (struct acpi_iort_named_component *)node->node_data;
		status = !strcmp(ncomp->device_name, buf.pointer) ?
							AE_OK : AE_NOT_FOUND;
			device_name = ncomp->device_name;
		} else {
			iwb = (struct acpi_iort_iwb *)node->node_data;
			device_name = iwb->device_name;
		}
		status = !strcmp(device_name, buf.pointer) ?  AE_OK : AE_NOT_FOUND;
		acpi_os_free(buf.pointer);
	} else if (node->type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX) {
		struct acpi_iort_root_complex *pci_rc;
@@ -317,12 +325,28 @@ static acpi_status iort_match_node_callback(struct acpi_iort_node *node,
	return status;
}

static acpi_status iort_match_iwb_callback(struct acpi_iort_node *node, void *context)
{
	struct acpi_iort_iwb *iwb;
	u32 *id = context;

	if (node->type != ACPI_IORT_NODE_IWB)
		return AE_NOT_FOUND;

	iwb = (struct acpi_iort_iwb *)node->node_data;
	if (iwb->iwb_index != *id)
		return AE_NOT_FOUND;

	return AE_OK;
}

static int iort_id_map(struct acpi_iort_id_mapping *map, u8 type, u32 rid_in,
		       u32 *rid_out, bool check_overlap)
{
	/* Single mapping does not care for input id */
	if (map->flags & ACPI_IORT_ID_SINGLE_MAPPING) {
		if (type == ACPI_IORT_NODE_NAMED_COMPONENT ||
		    type == ACPI_IORT_NODE_IWB		   ||
		    type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX) {
			*rid_out = map->output_base;
			return 0;
@@ -392,6 +416,7 @@ static struct acpi_iort_node *iort_node_get_id(struct acpi_iort_node *node,

	if (map->flags & ACPI_IORT_ID_SINGLE_MAPPING) {
		if (node->type == ACPI_IORT_NODE_NAMED_COMPONENT ||
		    node->type == ACPI_IORT_NODE_IWB ||
		    node->type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX ||
		    node->type == ACPI_IORT_NODE_SMMU_V3 ||
		    node->type == ACPI_IORT_NODE_PMCG) {
@@ -562,9 +587,14 @@ static struct acpi_iort_node *iort_find_dev_node(struct device *dev)
			return node;
		/*
		 * if not, then it should be a platform device defined in
		 * DSDT/SSDT (with Named Component node in IORT)
		 * DSDT/SSDT (with Named Component node in IORT) or an
		 * IWB device in the DSDT/SSDT.
		 */
		return iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT,
		node = iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT,
				      iort_match_node_callback, dev);
		if (node)
			return node;
		return iort_scan_node(ACPI_IORT_NODE_IWB,
				      iort_match_node_callback, dev);
	}

@@ -595,45 +625,45 @@ u32 iort_msi_map_id(struct device *dev, u32 input_id)
}

/**
 * iort_pmsi_get_dev_id() - Get the device id for a device
 * iort_msi_xlate() - Map a MSI input ID for a device
 * @dev: The device for which the mapping is to be done.
 * @dev_id: The device ID found.
 * @input_id: The device input ID.
 * @fwnode: Pointer to store the fwnode.
 *
 * Returns: 0 for successful find a dev id, -ENODEV on error
 * Returns: mapped MSI ID on success, input ID otherwise
 *	    On success, the fwnode pointer is initialized to the MSI
 *	    controller fwnode handle.
 */
int iort_pmsi_get_dev_id(struct device *dev, u32 *dev_id)
u32 iort_msi_xlate(struct device *dev, u32 input_id, struct fwnode_handle **fwnode)
{
	int i, index;
	struct acpi_iort_its_group *its;
	struct acpi_iort_node *node;
	u32 dev_id;

	node = iort_find_dev_node(dev);
	if (!node)
		return -ENODEV;
		return input_id;

	index = iort_get_id_mapping_index(node);
	/* if there is a valid index, go get the dev_id directly */
	if (index >= 0) {
		if (iort_node_get_id(node, dev_id, index))
			return 0;
	} else {
		for (i = 0; i < node->mapping_count; i++) {
			if (iort_node_map_platform_id(node, dev_id,
						      IORT_MSI_TYPE, i))
				return 0;
		}
	}
	node = iort_node_map_id(node, input_id, &dev_id, IORT_MSI_TYPE);
	if (!node)
		return input_id;

	return -ENODEV;
	/* Move to ITS specific data */
	its = (struct acpi_iort_its_group *)node->node_data;

	*fwnode = iort_find_domain_token(its->identifiers[0]);

	return dev_id;
}

static int __maybe_unused iort_find_its_base(u32 its_id, phys_addr_t *base)
int iort_its_translate_pa(struct fwnode_handle *node, phys_addr_t *base)
{
	struct iort_its_msi_chip *its_msi_chip;
	int ret = -ENODEV;

	spin_lock(&iort_msi_chip_lock);
	list_for_each_entry(its_msi_chip, &iort_msi_chip_list, list) {
		if (its_msi_chip->translation_id == its_id) {
		if (its_msi_chip->fw_node == node) {
			*base = its_msi_chip->base_addr;
			ret = 0;
			break;
@@ -644,6 +674,62 @@ static int __maybe_unused iort_find_its_base(u32 its_id, phys_addr_t *base)
	return ret;
}

static int __maybe_unused iort_find_its_base(u32 its_id, phys_addr_t *base)
{
	struct fwnode_handle *fwnode = iort_find_domain_token(its_id);

	if (!fwnode)
		return -ENODEV;

	return iort_its_translate_pa(fwnode, base);
}

/**
 * iort_pmsi_get_msi_info() - Get the device id and translate frame PA for a device
 * @dev: The device for which the mapping is to be done.
 * @dev_id: The device ID found.
 * @pa: optional pointer to store translate frame address.
 *
 * Returns: 0 for successful devid and pa retrieval, -ENODEV on error
 */
int iort_pmsi_get_msi_info(struct device *dev, u32 *dev_id, phys_addr_t *pa)
{
	struct acpi_iort_node *node, *parent = NULL;
	struct acpi_iort_its_group *its;
	int i, index;

	node = iort_find_dev_node(dev);
	if (!node)
		return -ENODEV;

	index = iort_get_id_mapping_index(node);
	/* if there is a valid index, go get the dev_id directly */
	if (index >= 0) {
		parent = iort_node_get_id(node, dev_id, index);
	} else {
		for (i = 0; i < node->mapping_count; i++) {
			parent = iort_node_map_platform_id(node, dev_id,
						      IORT_MSI_TYPE, i);
			if (parent)
				break;
		}
	}

	if (!parent)
		return -ENODEV;

	if (pa) {
		int ret;

		its = (struct acpi_iort_its_group *)node->node_data;
		ret = iort_find_its_base(its->identifiers[0], pa);
		if (ret)
			return ret;
	}

	return 0;
}

/**
 * iort_dev_find_its_id() - Find the ITS identifier for a device
 * @dev: The device.
@@ -703,6 +789,35 @@ struct irq_domain *iort_get_device_domain(struct device *dev, u32 id,
	return irq_find_matching_fwnode(handle, bus_token);
}

struct fwnode_handle *iort_iwb_handle(u32 iwb_id)
{
	struct fwnode_handle *fwnode;
	struct acpi_iort_node *node;
	struct acpi_device *device;
	struct acpi_iort_iwb *iwb;
	acpi_status status;
	acpi_handle handle;

	/* find its associated IWB node */
	node = iort_scan_node(ACPI_IORT_NODE_IWB, iort_match_iwb_callback, &iwb_id);
	if (!node)
		return NULL;

	iwb = (struct acpi_iort_iwb *)node->node_data;
	status = acpi_get_handle(NULL, iwb->device_name, &handle);
	if (ACPI_FAILURE(status))
		return NULL;

	device = acpi_get_acpi_dev(handle);
	if (!device)
		return NULL;

	fwnode = acpi_fwnode_handle(device);
	acpi_put_acpi_dev(device);

	return fwnode;
}

static void iort_set_device_domain(struct device *dev,
				   struct acpi_iort_node *node)
{
@@ -763,8 +878,14 @@ static struct irq_domain *iort_get_platform_device_domain(struct device *dev)
	/* find its associated iort node */
	node = iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT,
			      iort_match_node_callback, dev);
	if (!node) {
		/* find its associated iort node */
		node = iort_scan_node(ACPI_IORT_NODE_IWB,
				      iort_match_node_callback, dev);

		if (!node)
			return NULL;
	}

	/* then find its msi parent node */
	for (i = 0; i < node->mapping_count; i++) {
+3 −0
Original line number Diff line number Diff line
@@ -1197,6 +1197,9 @@ static int __init acpi_bus_init_irq(void)
	case ACPI_IRQ_MODEL_GIC:
		message = "GIC";
		break;
	case ACPI_IRQ_MODEL_GIC_V5:
		message = "GICv5";
		break;
	case ACPI_IRQ_MODEL_PLATFORM:
		message = "platform specific model";
		break;
+22 −21
Original line number Diff line number Diff line
@@ -19,18 +19,24 @@
				 MSI_FLAG_PCI_MSIX      |	\
				 MSI_FLAG_MULTI_PCI_MSI)

static int its_translate_frame_address(struct device_node *msi_node, phys_addr_t *pa)
static int its_translate_frame_address(struct fwnode_handle *msi_node, phys_addr_t *pa)
{
	struct resource res;
	int ret;

	ret = of_property_match_string(msi_node, "reg-names", "ns-translate");
	if (is_of_node(msi_node)) {
		struct device_node *msi_np = to_of_node(msi_node);

		ret = of_property_match_string(msi_np, "reg-names", "ns-translate");
		if (ret < 0)
			return ret;

	ret = of_address_to_resource(msi_node, ret, &res);
		ret = of_address_to_resource(msi_np, ret, &res);
		if (ret)
			return ret;
	} else {
		ret = iort_its_translate_pa(msi_node, &res.start);
	}

	*pa = res.start;
	return 0;
@@ -104,7 +110,7 @@ static int its_pci_msi_prepare(struct irq_domain *domain, struct device *dev,
static int its_v5_pci_msi_prepare(struct irq_domain *domain, struct device *dev,
				  int nvec, msi_alloc_info_t *info)
{
	struct device_node *msi_node = NULL;
	struct fwnode_handle *msi_node = NULL;
	struct msi_domain_info *msi_info;
	struct pci_dev *pdev;
	phys_addr_t pa;
@@ -116,7 +122,7 @@ static int its_v5_pci_msi_prepare(struct irq_domain *domain, struct device *dev,

	pdev = to_pci_dev(dev);

	rid = pci_msi_map_rid_ctlr_node(pdev, &msi_node);
	rid = pci_msi_map_rid_ctlr_node(domain->parent, pdev, &msi_node);
	if (!msi_node)
		return -ENODEV;

@@ -124,7 +130,7 @@ static int its_v5_pci_msi_prepare(struct irq_domain *domain, struct device *dev,
	if (ret)
		return -ENODEV;

	of_node_put(msi_node);
	fwnode_handle_put(msi_node);

	/* ITS specific DeviceID */
	info->scratchpad[0].ul = rid;
@@ -161,7 +167,7 @@ static int of_pmsi_get_msi_info(struct irq_domain *domain, struct device *dev, u
				ret = -EINVAL;

			if (!ret && pa)
				ret = its_translate_frame_address(it.node, pa);
				ret = its_translate_frame_address(of_fwnode_handle(it.node), pa);

			if (!ret)
				*dev_id = args;
@@ -176,11 +182,6 @@ static int of_pmsi_get_msi_info(struct irq_domain *domain, struct device *dev, u
	return of_map_id(dev->of_node, dev->id, "msi-map", "msi-map-mask", &msi_ctrl, dev_id);
}

int __weak iort_pmsi_get_dev_id(struct device *dev, u32 *dev_id)
{
	return -1;
}

static int its_pmsi_prepare(struct irq_domain *domain, struct device *dev,
			    int nvec, msi_alloc_info_t *info)
{
@@ -191,7 +192,7 @@ static int its_pmsi_prepare(struct irq_domain *domain, struct device *dev,
	if (dev->of_node)
		ret = of_pmsi_get_msi_info(domain->parent, dev, &dev_id, NULL);
	else
		ret = iort_pmsi_get_dev_id(dev, &dev_id);
		ret = iort_pmsi_get_msi_info(dev, &dev_id, NULL);
	if (ret)
		return ret;

@@ -214,10 +215,10 @@ static int its_v5_pmsi_prepare(struct irq_domain *domain, struct device *dev,
	u32 dev_id;
	int ret;

	if (!dev->of_node)
		return -ENODEV;

	if (dev->of_node)
		ret = of_pmsi_get_msi_info(domain->parent, dev, &dev_id, &pa);
	else
		ret = iort_pmsi_get_msi_info(dev, &dev_id, &pa);
	if (ret)
		return ret;

+183 −44
Original line number Diff line number Diff line
@@ -5,6 +5,7 @@

#define pr_fmt(fmt)	"GICv5 IRS: " fmt

#include <linux/acpi.h>
#include <linux/kmemleak.h>
#include <linux/log2.h>
#include <linux/of.h>
@@ -545,15 +546,13 @@ int gicv5_irs_register_cpu(int cpuid)

static void __init gicv5_irs_init_bases(struct gicv5_irs_chip_data *irs_data,
					void __iomem *irs_base,
					struct fwnode_handle *handle)
					bool noncoherent)
{
	struct device_node *np = to_of_node(handle);
	u32 cr0, cr1;

	irs_data->fwnode = handle;
	irs_data->irs_base = irs_base;

	if (of_property_read_bool(np, "dma-noncoherent")) {
	if (noncoherent) {
		/*
		 * A non-coherent IRS implies that some cache levels cannot be
		 * used coherently by the cores and GIC. Our only option is to mark
@@ -678,12 +677,52 @@ static void irs_setup_pri_bits(u32 idr1)
	}
}

static int __init gicv5_irs_init(struct device_node *node)
static int __init gicv5_irs_init(struct gicv5_irs_chip_data *irs_data)
{
	u32 spi_count, idr = irs_readl_relaxed(irs_data, GICV5_IRS_IDR2);

	if (WARN(!FIELD_GET(GICV5_IRS_IDR2_LPI, idr),
		 "LPI support not available - no IPIs, can't proceed\n")) {
		return -ENODEV;
	}

	idr = irs_readl_relaxed(irs_data, GICV5_IRS_IDR7);
	irs_data->spi_min = FIELD_GET(GICV5_IRS_IDR7_SPI_BASE, idr);

	idr = irs_readl_relaxed(irs_data, GICV5_IRS_IDR6);
	irs_data->spi_range = FIELD_GET(GICV5_IRS_IDR6_SPI_IRS_RANGE, idr);

	/*
	 * Do the global setting only on the first IRS.
	 * Global properties (iaffid_bits, global spi count) are guaranteed to
	 * be consistent across IRSes by the architecture.
	 */
	if (list_empty(&irs_nodes)) {

		idr = irs_readl_relaxed(irs_data, GICV5_IRS_IDR1);
		irs_setup_pri_bits(idr);

		idr = irs_readl_relaxed(irs_data, GICV5_IRS_IDR5);

		spi_count = FIELD_GET(GICV5_IRS_IDR5_SPI_RANGE, idr);
		gicv5_global_data.global_spi_count = spi_count;

		gicv5_init_lpi_domain();

		pr_debug("Detected %u SPIs globally\n", spi_count);
	}

	list_add_tail(&irs_data->entry, &irs_nodes);

	return 0;
}

static int __init gicv5_irs_of_init(struct device_node *node)
{
	struct gicv5_irs_chip_data *irs_data;
	void __iomem *irs_base;
	u32 idr, spi_count;
	u8 iaffid_bits;
	u32 idr;
	int ret;

	irs_data = kzalloc(sizeof(*irs_data), GFP_KERNEL);
@@ -705,7 +744,8 @@ static int __init gicv5_irs_init(struct device_node *node)
		goto out_err;
	}

	gicv5_irs_init_bases(irs_data, irs_base, &node->fwnode);
	irs_data->fwnode = of_fwnode_handle(node);
	gicv5_irs_init_bases(irs_data, irs_base, of_property_read_bool(node, "dma-noncoherent"));

	idr = irs_readl_relaxed(irs_data, GICV5_IRS_IDR1);
	iaffid_bits = FIELD_GET(GICV5_IRS_IDR1_IAFFID_BITS, idr) + 1;
@@ -716,18 +756,9 @@ static int __init gicv5_irs_init(struct device_node *node)
		goto out_iomem;
	}

	idr = irs_readl_relaxed(irs_data, GICV5_IRS_IDR2);
	if (WARN(!FIELD_GET(GICV5_IRS_IDR2_LPI, idr),
		 "LPI support not available - no IPIs, can't proceed\n")) {
		ret = -ENODEV;
	ret = gicv5_irs_init(irs_data);
	if (ret)
		goto out_iomem;
	}

	idr = irs_readl_relaxed(irs_data, GICV5_IRS_IDR7);
	irs_data->spi_min = FIELD_GET(GICV5_IRS_IDR7_SPI_BASE, idr);

	idr = irs_readl_relaxed(irs_data, GICV5_IRS_IDR6);
	irs_data->spi_range = FIELD_GET(GICV5_IRS_IDR6_SPI_IRS_RANGE, idr);

	if (irs_data->spi_range) {
		pr_info("%s detected SPI range [%u-%u]\n",
@@ -737,29 +768,7 @@ static int __init gicv5_irs_init(struct device_node *node)
						irs_data->spi_range - 1);
	}

	/*
	 * Do the global setting only on the first IRS.
	 * Global properties (iaffid_bits, global spi count) are guaranteed to
	 * be consistent across IRSes by the architecture.
	 */
	if (list_empty(&irs_nodes)) {

		idr = irs_readl_relaxed(irs_data, GICV5_IRS_IDR1);
		irs_setup_pri_bits(idr);

		idr = irs_readl_relaxed(irs_data, GICV5_IRS_IDR5);

		spi_count = FIELD_GET(GICV5_IRS_IDR5_SPI_RANGE, idr);
		gicv5_global_data.global_spi_count = spi_count;

		gicv5_init_lpi_domain();

		pr_debug("Detected %u SPIs globally\n", spi_count);
	}

	list_add_tail(&irs_data->entry, &irs_nodes);

	return 0;
	return ret;

out_iomem:
	iounmap(irs_base);
@@ -805,8 +814,11 @@ void __init gicv5_irs_its_probe(void)
{
	struct gicv5_irs_chip_data *irs_data;

	if (acpi_disabled)
		list_for_each_entry(irs_data, &irs_nodes, entry)
			gicv5_its_of_probe(to_of_node(irs_data->fwnode));
	else
		gicv5_its_acpi_probe();
}

int __init gicv5_irs_of_probe(struct device_node *parent)
@@ -818,10 +830,137 @@ int __init gicv5_irs_of_probe(struct device_node *parent)
		if (!of_device_is_compatible(np, "arm,gic-v5-irs"))
			continue;

		ret = gicv5_irs_init(np);
		ret = gicv5_irs_of_init(np);
		if (ret)
			pr_err("Failed to init IRS %s\n", np->full_name);
	}

	return list_empty(&irs_nodes) ? -ENODEV : 0;
}

#ifdef CONFIG_ACPI

#define ACPI_GICV5_IRS_MEM_SIZE (SZ_64K)
static struct gicv5_irs_chip_data *current_irs_data __initdata;
static int current_irsid __initdata = -1;
static u8 current_iaffid_bits __initdata;

static int __init gic_acpi_parse_iaffid(union acpi_subtable_headers *header,
					const unsigned long end)
{
	struct acpi_madt_generic_interrupt *gicc = (struct acpi_madt_generic_interrupt *)header;
	int cpu;

	if (!(gicc->flags & (ACPI_MADT_ENABLED | ACPI_MADT_GICC_ONLINE_CAPABLE)))
		return 0;

	if (gicc->irs_id != current_irsid)
		return 0;

	cpu = get_logical_index(gicc->arm_mpidr);

	if (gicc->iaffid & ~GENMASK(current_iaffid_bits - 1, 0)) {
		pr_warn("CPU %d iaffid 0x%x exceeds IRS iaffid bits\n", cpu, gicc->iaffid);
		return 0;
	}

	/* Bind the IAFFID and the CPU */
	per_cpu(cpu_iaffid, cpu).iaffid = gicc->iaffid;
	per_cpu(cpu_iaffid, cpu).valid = true;
	pr_debug("Processed IAFFID %u for CPU%d", per_cpu(cpu_iaffid, cpu).iaffid, cpu);

	/* We also know that the CPU is connected to this IRS */
	per_cpu(per_cpu_irs_data, cpu) = current_irs_data;

	return 0;
}

static int __init gicv5_irs_acpi_init_affinity(u32 irsid, struct gicv5_irs_chip_data *irs_data)
{
	u32 idr;

	current_irsid = irsid;
	current_irs_data = irs_data;

	idr = irs_readl_relaxed(irs_data, GICV5_IRS_IDR1);
	current_iaffid_bits = FIELD_GET(GICV5_IRS_IDR1_IAFFID_BITS, idr) + 1;

	acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_INTERRUPT, gic_acpi_parse_iaffid, 0);

	return 0;
}

static struct resource * __init gic_request_region(resource_size_t base, resource_size_t size,
						   const char *name)
{
	struct resource *r = request_mem_region(base, size, name);

	if (!r)
		pr_warn_once(FW_BUG "%s region %pa has overlapping address\n", name, &base);

	return r;
}

static int __init gic_acpi_parse_madt_irs(union acpi_subtable_headers *header,
					  const unsigned long end)
{
	struct acpi_madt_gicv5_irs *irs = (struct acpi_madt_gicv5_irs *)header;
	struct gicv5_irs_chip_data *irs_data;
	void __iomem *irs_base;
	struct resource *r;
	int ret;

	/* Per-IRS data structure */
	irs_data = kzalloc(sizeof(*irs_data), GFP_KERNEL);
	if (!irs_data)
		return -ENOMEM;

	/* This spinlock is used for SPI config changes */
	raw_spin_lock_init(&irs_data->spi_config_lock);

	r = gic_request_region(irs->config_base_address, ACPI_GICV5_IRS_MEM_SIZE, "GICv5 IRS");
	if (!r) {
		ret = -EBUSY;
		goto out_free;
	}

	irs_base = ioremap(irs->config_base_address, ACPI_GICV5_IRS_MEM_SIZE);
	if (!irs_base) {
		pr_err("Unable to map GIC IRS registers\n");
		ret = -ENOMEM;
		goto out_release;
	}

	gicv5_irs_init_bases(irs_data, irs_base, irs->flags & ACPI_MADT_IRS_NON_COHERENT);

	gicv5_irs_acpi_init_affinity(irs->irs_id, irs_data);

	ret = gicv5_irs_init(irs_data);
	if (ret)
		goto out_map;

	if (irs_data->spi_range) {
		pr_info("%s @%llx detected SPI range [%u-%u]\n", "IRS", irs->config_base_address,
									irs_data->spi_min,
									irs_data->spi_min +
									irs_data->spi_range - 1);
	}

	return 0;

out_map:
	iounmap(irs_base);
out_release:
	release_mem_region(r->start, resource_size(r));
out_free:
	kfree(irs_data);
	return ret;
}

int __init gicv5_irs_acpi_probe(void)
{
	acpi_table_parse_madt(ACPI_MADT_TYPE_GICV5_IRS, gic_acpi_parse_madt_irs, 0);

	return list_empty(&irs_nodes) ? -ENODEV : 0;
}
#endif
+130 −2
Original line number Diff line number Diff line
@@ -5,6 +5,8 @@

#define pr_fmt(fmt)	"GICv5 ITS: " fmt

#include <linux/acpi.h>
#include <linux/acpi_iort.h>
#include <linux/bitmap.h>
#include <linux/iommu.h>
#include <linux/init.h>
@@ -1115,7 +1117,7 @@ static int gicv5_its_init_domain(struct gicv5_its_chip_data *its, struct irq_dom
}

static int __init gicv5_its_init_bases(void __iomem *its_base, struct fwnode_handle *handle,
				       struct irq_domain *parent_domain)
				       struct irq_domain *parent_domain, bool noncoherent)
{
	struct device_node *np = to_of_node(handle);
	struct gicv5_its_chip_data *its_node;
@@ -1208,7 +1210,8 @@ static int __init gicv5_its_init(struct device_node *node)
	}

	ret = gicv5_its_init_bases(its_base, of_fwnode_handle(node),
				   gicv5_global_data.lpi_domain);
				   gicv5_global_data.lpi_domain,
				   of_property_read_bool(node, "dma-noncoherent"));
	if (ret)
		goto out_unmap;

@@ -1231,3 +1234,128 @@ void __init gicv5_its_of_probe(struct device_node *parent)
			pr_err("Failed to init ITS %s\n", np->full_name);
	}
}

#ifdef CONFIG_ACPI

#define ACPI_GICV5_ITS_MEM_SIZE (SZ_64K)

static struct acpi_madt_gicv5_translator *current_its_entry __initdata;
static struct fwnode_handle *current_its_fwnode __initdata;

static int __init gic_acpi_parse_madt_its_translate(union acpi_subtable_headers *header,
						    const unsigned long end)
{
	struct acpi_madt_gicv5_translate_frame *its_frame;
	struct fwnode_handle *msi_dom_handle;
	struct resource res = {};
	int err;

	its_frame = (struct acpi_madt_gicv5_translate_frame *)header;
	if (its_frame->linked_translator_id != current_its_entry->translator_id)
		return 0;

	res.start = its_frame->base_address;
	res.end = its_frame->base_address + ACPI_GICV5_ITS_MEM_SIZE - 1;
	res.flags = IORESOURCE_MEM;

	msi_dom_handle = irq_domain_alloc_parented_fwnode(&res.start, current_its_fwnode);
	if (!msi_dom_handle) {
		pr_err("ITS@%pa: Unable to allocate GICv5 ITS translate domain token\n",
		       &res.start);
		return -ENOMEM;
	}

	err = iort_register_domain_token(its_frame->translate_frame_id, res.start,
					 msi_dom_handle);
	if (err) {
		pr_err("ITS@%pa: Unable to register GICv5 ITS domain token (ITS TRANSLATE FRAME ID %d) to IORT\n",
		       &res.start, its_frame->translate_frame_id);
		irq_domain_free_fwnode(msi_dom_handle);
		return err;
	}

	return 0;
}

static int __init gic_acpi_free_madt_its_translate(union acpi_subtable_headers *header,
						   const unsigned long end)
{
	struct acpi_madt_gicv5_translate_frame *its_frame;
	struct fwnode_handle *msi_dom_handle;

	its_frame = (struct acpi_madt_gicv5_translate_frame *)header;
	if (its_frame->linked_translator_id != current_its_entry->translator_id)
		return 0;

	msi_dom_handle = iort_find_domain_token(its_frame->translate_frame_id);
	if (!msi_dom_handle)
		return 0;

	iort_deregister_domain_token(its_frame->translate_frame_id);
	irq_domain_free_fwnode(msi_dom_handle);

	return 0;
}

static int __init gic_acpi_parse_madt_its(union acpi_subtable_headers *header,
					  const unsigned long end)
{
	struct acpi_madt_gicv5_translator *its_entry;
	struct fwnode_handle *dom_handle;
	struct resource res = {};
	void __iomem *its_base;
	int err;

	its_entry = (struct acpi_madt_gicv5_translator *)header;
	res.start = its_entry->base_address;
	res.end = its_entry->base_address + ACPI_GICV5_ITS_MEM_SIZE - 1;
	res.flags = IORESOURCE_MEM;

	if (!request_mem_region(res.start, resource_size(&res), "GICv5 ITS"))
		return -EBUSY;

	dom_handle = irq_domain_alloc_fwnode(&res.start);
	if (!dom_handle) {
		pr_err("ITS@%pa: Unable to allocate GICv5 ITS domain token\n",
		       &res.start);
		err = -ENOMEM;
		goto out_rel_res;
	}

	current_its_entry = its_entry;
	current_its_fwnode = dom_handle;

	acpi_table_parse_madt(ACPI_MADT_TYPE_GICV5_ITS_TRANSLATE,
			      gic_acpi_parse_madt_its_translate, 0);

	its_base = ioremap(res.start, ACPI_GICV5_ITS_MEM_SIZE);
	if (!its_base) {
		err = -ENOMEM;
		goto out_unregister;
	}

	err = gicv5_its_init_bases(its_base, dom_handle, gicv5_global_data.lpi_domain,
				   its_entry->flags & ACPI_MADT_GICV5_ITS_NON_COHERENT);
	if (err)
		goto out_unmap;

	return 0;

out_unmap:
	iounmap(its_base);
out_unregister:
	acpi_table_parse_madt(ACPI_MADT_TYPE_GICV5_ITS_TRANSLATE,
			      gic_acpi_free_madt_its_translate, 0);
	irq_domain_free_fwnode(dom_handle);
out_rel_res:
	release_mem_region(res.start, resource_size(&res));
	return err;
}

void __init gicv5_its_acpi_probe(void)
{
	acpi_table_parse_madt(ACPI_MADT_TYPE_GICV5_ITS, gic_acpi_parse_madt_its, 0);
}
#else
void __init gicv5_its_acpi_probe(void) { }
#endif
Loading