static acpi_status handle_ioapic_add()

in ioapic.c [96:199]


static acpi_status handle_ioapic_add(acpi_handle handle, u32 lvl,
				     void *context, void **rv)
{
	acpi_status status;
	unsigned long long gsi_base;
	struct acpi_pci_ioapic *ioapic;
	struct pci_dev *dev = NULL;
	struct resource *res = NULL, *pci_res = NULL, *crs_res;
	char *type = NULL;

	if (!acpi_is_ioapic(handle, &type))
		return AE_OK;

	mutex_lock(&ioapic_list_lock);
	list_for_each_entry(ioapic, &ioapic_list, list)
		if (ioapic->handle == handle) {
			mutex_unlock(&ioapic_list_lock);
			return AE_OK;
		}

	status = acpi_evaluate_integer(handle, "_GSB", NULL, &gsi_base);
	if (ACPI_FAILURE(status)) {
		acpi_handle_warn(handle, "failed to evaluate _GSB method\n");
		goto exit;
	}

	ioapic = kzalloc(sizeof(*ioapic), GFP_KERNEL);
	if (!ioapic) {
		pr_err("cannot allocate memory for new IOAPIC\n");
		goto exit;
	} else {
		ioapic->root_handle = (acpi_handle)context;
		ioapic->handle = handle;
		ioapic->gsi_base = (u32)gsi_base;
		INIT_LIST_HEAD(&ioapic->list);
	}

	if (acpi_ioapic_registered(handle, (u32)gsi_base))
		goto done;

	dev = acpi_get_pci_dev(handle);
	if (dev && pci_resource_len(dev, 0)) {
		if (pci_enable_device(dev) < 0)
			goto exit_put;
		pci_set_master(dev);
		if (pci_request_region(dev, 0, type))
			goto exit_disable;
		pci_res = &dev->resource[0];
		ioapic->pdev = dev;
	} else {
		pci_dev_put(dev);
		dev = NULL;
	}

	crs_res = &ioapic->res;
	acpi_walk_resources(handle, METHOD_NAME__CRS, setup_res, crs_res);
	crs_res->name = type;
	crs_res->flags |= IORESOURCE_BUSY;
	if (crs_res->flags == 0) {
		acpi_handle_warn(handle, "failed to get resource\n");
		goto exit_release;
	} else if (insert_resource(&iomem_resource, crs_res)) {
		acpi_handle_warn(handle, "failed to insert resource\n");
		goto exit_release;
	}

	/* try pci resource first, then "_CRS" resource */
	res = pci_res;
	if (!res || !res->flags)
		res = crs_res;

	if (acpi_register_ioapic(handle, res->start, (u32)gsi_base)) {
		acpi_handle_warn(handle, "failed to register IOAPIC\n");
		goto exit_release;
	}
done:
	list_add(&ioapic->list, &ioapic_list);
	mutex_unlock(&ioapic_list_lock);

	if (dev)
		dev_info(&dev->dev, "%s at %pR, GSI %u\n",
			 type, res, (u32)gsi_base);
	else
		acpi_handle_info(handle, "%s at %pR, GSI %u\n",
				 type, res, (u32)gsi_base);

	return AE_OK;

exit_release:
	if (dev)
		pci_release_region(dev, 0);
	if (ioapic->res.flags && ioapic->res.parent)
		release_resource(&ioapic->res);
exit_disable:
	if (dev)
		pci_disable_device(dev);
exit_put:
	pci_dev_put(dev);
	kfree(ioapic);
exit:
	mutex_unlock(&ioapic_list_lock);
	*(acpi_status *)rv = AE_ERROR;
	return AE_OK;
}