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

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

ACPI / dock: Use callback pointers from devices' ACPI hotplug contexts



Instead of requiring a set of special dock operations to be registered
via register_hotplug_dock_device() for each ACPI dock device, it is
much more straightforward to use callback pointers from the devices'
hotplug contexts if available.

For this reason, modify dock_hotplug_event() to use callback pointers
from the hotplug contexts of ACPI devices and fall back to using the
special dock operarions only if those callbacks are missing.  Also
make the ACPI-based PCI hotplug (ACPIPHP) subsystem set the .fixup
callback pointer in the hotplug contexts of devices handled by it to
a new function, acpiphp_post_dock_fixup(), so that the dock station
driver can use the callbacks from those contexts instead of special
dock operations registered via register_hotplug_dock_device().

Along with the above changes drop the ACPIPHP's dock operations that
are not necessary any more.

Signed-off-by: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
parent 3b52b21f
Loading
Loading
Loading
Loading
+29 −0
Original line number Diff line number Diff line
@@ -185,9 +185,38 @@ static void dock_release_hotplug(struct dock_dependent_device *dd)
static void dock_hotplug_event(struct dock_dependent_device *dd, u32 event,
			       enum dock_callback_type cb_type)
{
	struct acpi_device *adev = dd->adev;
	acpi_notify_handler cb = NULL;
	bool run = false;

	acpi_lock_hp_context();

	if (!adev->hp)
		goto no_context;

	if (cb_type == DOCK_CALL_FIXUP) {
		void (*fixup)(struct acpi_device *);

		fixup = adev->hp->fixup;
		if (fixup) {
			acpi_unlock_hp_context();
			fixup(adev);
			return;
		}
	} else {
		int (*notify)(struct acpi_device *, u32);

		notify = adev->hp->event;
		if (notify) {
			acpi_unlock_hp_context();
			notify(adev, event);
			return;
		}
	}

 no_context:
	acpi_unlock_hp_context();

	mutex_lock(&hotplug_lock);

	if (dd->hp_context) {
+49 −70
Original line number Diff line number Diff line
@@ -60,6 +60,7 @@ static LIST_HEAD(bridge_list);
static DEFINE_MUTEX(bridge_mutex);

static int acpiphp_hotplug_event(struct acpi_device *adev, u32 type);
static void acpiphp_post_dock_fixup(struct acpi_device *adev);
static void acpiphp_sanitize_bus(struct pci_bus *bus);
static void acpiphp_set_hpp_values(struct pci_bus *bus);
static void hotplug_event(u32 type, struct acpiphp_context *context);
@@ -80,7 +81,8 @@ static struct acpiphp_context *acpiphp_init_context(struct acpi_device *adev)
		return NULL;

	context->refcount = 1;
	acpi_set_hp_context(adev, &context->hp, acpiphp_hotplug_event, NULL);
	acpi_set_hp_context(adev, &context->hp, acpiphp_hotplug_event,
			    acpiphp_post_dock_fixup);
	return context;
}

@@ -130,6 +132,27 @@ static inline void put_bridge(struct acpiphp_bridge *bridge)
	kref_put(&bridge->ref, free_bridge);
}

static struct acpiphp_context *acpiphp_grab_context(struct acpi_device *adev)
{
	struct acpiphp_context *context;

	acpi_lock_hp_context();
	context = acpiphp_get_context(adev);
	if (!context || context->func.parent->is_going_away) {
		acpi_unlock_hp_context();
		return NULL;
	}
	get_bridge(context->func.parent);
	acpiphp_put_context(context);
	acpi_unlock_hp_context();
	return context;
}

static void acpiphp_let_context_go(struct acpiphp_context *context)
{
	put_bridge(context->func.parent);
}

static void free_bridge(struct kref *kref)
{
	struct acpiphp_context *context;
@@ -164,28 +187,29 @@ static void free_bridge(struct kref *kref)
	acpi_unlock_hp_context();
}

/*
 * the _DCK method can do funny things... and sometimes not
 * hah-hah funny.
/**
 * acpiphp_post_dock_fixup - Post-dock fixups for PCI devices.
 * @adev: ACPI device object corresponding to a PCI device.
 *
 * TBD - figure out a way to only call fixups for
 * systems that require them.
 * TBD - figure out a way to only call fixups for systems that require them.
 */
static void post_dock_fixups(acpi_handle not_used, u32 event, void *data)
static void acpiphp_post_dock_fixup(struct acpi_device *adev)
{
	struct acpiphp_context *context = data;
	struct pci_bus *bus = context->func.slot->bus;
	struct acpiphp_context *context = acpiphp_grab_context(adev);
	struct pci_bus *bus;
	u32 buses;

	if (!bus->self)
	if (!context)
		return;

	bus = context->func.slot->bus;
	if (!bus->self)
		goto out;

	/* fixup bad _DCK function that rewrites
	 * secondary bridge on slot
	 */
	pci_read_config_dword(bus->self,
			PCI_PRIMARY_BUS,
			&buses);
	pci_read_config_dword(bus->self, PCI_PRIMARY_BUS, &buses);

	if (((buses >> 8) & 0xff) != bus->busn_res.start) {
		buses = (buses & 0xff000000)
@@ -194,24 +218,11 @@ static void post_dock_fixups(acpi_handle not_used, u32 event, void *data)
			| ((unsigned int)(bus->busn_res.end) << 16);
		pci_write_config_dword(bus->self, PCI_PRIMARY_BUS, buses);
	}
}

static void dock_event(acpi_handle handle, u32 type, void *data)
{
	struct acpi_device *adev;

	adev = acpi_bus_get_acpi_device(handle);
	if (adev) {
		acpiphp_hotplug_event(adev, type);
		acpi_bus_put_acpi_device(adev);
	}
 out:
	acpiphp_let_context_go(context);
}

static const struct acpi_dock_ops acpiphp_dock_ops = {
	.fixup = post_dock_fixups,
	.handler = dock_event,
};

/* Check whether the PCI device is managed by native PCIe hotplug driver */
static bool device_is_managed_by_native_pciehp(struct pci_dev *pdev)
{
@@ -241,20 +252,6 @@ static bool device_is_managed_by_native_pciehp(struct pci_dev *pdev)
	return true;
}

static void acpiphp_dock_init(void *data)
{
	struct acpiphp_context *context = data;

	get_bridge(context->func.parent);
}

static void acpiphp_dock_release(void *data)
{
	struct acpiphp_context *context = data;

	put_bridge(context->func.parent);
}

/**
 * acpiphp_add_context - Add ACPIPHP context to an ACPI device object.
 * @handle: ACPI handle of the object to add a context to.
@@ -300,15 +297,18 @@ static acpi_status acpiphp_add_context(acpi_handle handle, u32 lvl, void *data,
	newfunc = &context->func;
	newfunc->function = function;
	newfunc->parent = bridge;
	acpi_unlock_hp_context();

	if (acpi_has_method(handle, "_EJ0"))
	/*
	 * If this is a dock device, its _EJ0 should be executed by the dock
	 * notify handler after calling _DCK.
	 */
	if (!is_dock_device(adev) && acpi_has_method(handle, "_EJ0"))
		newfunc->flags = FUNC_HAS_EJ0;

	if (acpi_has_method(handle, "_STA"))
		newfunc->flags |= FUNC_HAS_STA;

	acpi_unlock_hp_context();

	/* search for objects that share the same slot */
	list_for_each_entry(slot, &bridge->slots, node)
		if (slot->device == device)
@@ -369,18 +369,6 @@ static acpi_status acpiphp_add_context(acpi_handle handle, u32 lvl, void *data,
				       &val, 60*1000))
		slot->flags |= SLOT_ENABLED;

	if (is_dock_device(adev)) {
		/* we don't want to call this device's _EJ0
		 * because we want the dock notify handler
		 * to call it after it calls _DCK
		 */
		newfunc->flags &= ~FUNC_HAS_EJ0;
		if (register_hotplug_dock_device(handle,
			&acpiphp_dock_ops, context,
			acpiphp_dock_init, acpiphp_dock_release))
			pr_debug("failed to register dock device\n");
	}

	return AE_OK;
}

@@ -411,11 +399,9 @@ static void cleanup_bridge(struct acpiphp_bridge *bridge)
		list_for_each_entry(func, &slot->funcs, sibling) {
			struct acpi_device *adev = func_to_acpi_device(func);

			if (is_dock_device(adev))
				unregister_hotplug_dock_device(adev->handle);

			acpi_lock_hp_context();
			adev->hp->event = NULL;
			adev->hp->fixup = NULL;
			acpi_unlock_hp_context();
		}
		slot->flags |= SLOT_IS_GOING_AWAY;
@@ -851,19 +837,12 @@ static int acpiphp_hotplug_event(struct acpi_device *adev, u32 type)
{
	struct acpiphp_context *context;

	acpi_lock_hp_context();
	context = acpiphp_get_context(adev);
	if (!context || context->func.parent->is_going_away) {
		acpi_unlock_hp_context();
	context = acpiphp_grab_context(adev);
	if (!context)
		return -ENODATA;
	}
	get_bridge(context->func.parent);
	acpiphp_put_context(context);
	acpi_unlock_hp_context();

	hotplug_event(type, context);

	put_bridge(context->func.parent);
	acpiphp_let_context_go(context);
	return 0;
}