Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 600812ec authored by Satoru Takeuchi's avatar Satoru Takeuchi Committed by Greg Kroah-Hartman
Browse files

acpiphp: add support for ioapic hot-remove

parent 23186279
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -150,6 +150,11 @@ struct acpiphp_attention_info
	struct module *owner;
};

struct acpiphp_ioapic {
	struct pci_dev *dev;
	u32 gsi_base;
	struct list_head list;
};

/* PCI bus bridge HID */
#define ACPI_PCI_HOST_HID		"PNP0A03"
+87 −14
Original line number Diff line number Diff line
@@ -53,6 +53,8 @@
#include "acpiphp.h"

static LIST_HEAD(bridge_list);
static LIST_HEAD(ioapic_list);
static DEFINE_SPINLOCK(ioapic_list_lock);

#define MY_NAME "acpiphp_glue"

@@ -797,6 +799,7 @@ ioapic_add(acpi_handle handle, u32 lvl, void *context, void **rv)
	struct pci_dev *pdev;
	u32 gsi_base;
	u64 phys_addr;
	struct acpiphp_ioapic *ioapic;

	/* Evaluate _STA if present */
	status = acpi_evaluate_integer(handle, "_STA", NULL, &sta);
@@ -811,30 +814,87 @@ ioapic_add(acpi_handle handle, u32 lvl, void *context, void **rv)
	if (get_gsi_base(handle, &gsi_base))
		return AE_OK;

	ioapic = kmalloc(sizeof(*ioapic), GFP_KERNEL);
	if (!ioapic)
		return AE_NO_MEMORY;

	pdev = get_apic_pci_info(handle);
	if (!pdev)
		return AE_OK;
		goto exit_kfree;

	if (pci_enable_device(pdev)) {
		pci_dev_put(pdev);
		return AE_OK;
	}
	if (pci_enable_device(pdev))
		goto exit_pci_dev_put;

	pci_set_master(pdev);

	if (pci_request_region(pdev, 0, "I/O APIC(acpiphp)")) {
		pci_disable_device(pdev);
		pci_dev_put(pdev);
		return AE_OK;
	}
	if (pci_request_region(pdev, 0, "I/O APIC(acpiphp)"))
		goto exit_pci_disable_device;

	phys_addr = pci_resource_start(pdev, 0);
	if (acpi_register_ioapic(handle, phys_addr, gsi_base)) {
	if (acpi_register_ioapic(handle, phys_addr, gsi_base))
		goto exit_pci_release_region;

	ioapic->gsi_base = gsi_base;
	ioapic->dev = pdev;
	spin_lock(&ioapic_list_lock);
	list_add_tail(&ioapic->list, &ioapic_list);
	spin_unlock(&ioapic_list_lock);

	return AE_OK;

 exit_pci_release_region:
	pci_release_region(pdev, 0);
 exit_pci_disable_device:
	pci_disable_device(pdev);
 exit_pci_dev_put:
	pci_dev_put(pdev);
 exit_kfree:
	kfree(ioapic);

	return AE_OK;
}

static acpi_status
ioapic_remove(acpi_handle handle, u32 lvl, void *context, void **rv)
{
	acpi_status status;
	unsigned long sta;
	acpi_handle tmp;
	u32 gsi_base;
	struct acpiphp_ioapic *pos, *n, *ioapic = NULL;

	/* Evaluate _STA if present */
	status = acpi_evaluate_integer(handle, "_STA", NULL, &sta);
	if (ACPI_SUCCESS(status) && sta != ACPI_STA_ALL)
		return AE_CTRL_DEPTH;

	/* Scan only PCI bus scope */
	status = acpi_get_handle(handle, "_HID", &tmp);
	if (ACPI_SUCCESS(status))
		return AE_CTRL_DEPTH;

	if (get_gsi_base(handle, &gsi_base))
		return AE_OK;

	acpi_unregister_ioapic(handle, gsi_base);

	spin_lock(&ioapic_list_lock);
	list_for_each_entry_safe(pos, n, &ioapic_list, list) {
		if (pos->gsi_base != gsi_base)
			continue;
		ioapic = pos;
		list_del(&ioapic->list);
		break;
	}
	spin_unlock(&ioapic_list_lock);

	if (!ioapic)
		return AE_OK;

	pci_release_region(ioapic->dev, 0);
	pci_disable_device(ioapic->dev);
	pci_dev_put(ioapic->dev);
	kfree(ioapic);

	return AE_OK;
}
@@ -847,6 +907,14 @@ static int acpiphp_configure_ioapics(acpi_handle handle)
	return 0;
}

static int acpiphp_unconfigure_ioapics(acpi_handle handle)
{
	ioapic_remove(handle, 0, NULL, NULL);
	acpi_walk_namespace(ACPI_TYPE_DEVICE, handle,
			    ACPI_UINT32_MAX, ioapic_remove, NULL, NULL);
	return 0;
}

static int power_on_slot(struct acpiphp_slot *slot)
{
	acpi_status status;
@@ -1146,7 +1214,12 @@ static int disable_device(struct acpiphp_slot *slot)
				pci_disable_device(func->pci_dev);
			}
		}
	}

	list_for_each (l, &slot->funcs) {
		func = list_entry(l, struct acpiphp_func, sibling);

		acpiphp_unconfigure_ioapics(func->handle);
		acpiphp_bus_trim(func->handle);
		/* try to remove anyway.
		 * acpiphp_bus_add might have been failed */