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

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
...@@ -185,9 +185,38 @@ static void dock_release_hotplug(struct dock_dependent_device *dd) ...@@ -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, static void dock_hotplug_event(struct dock_dependent_device *dd, u32 event,
enum dock_callback_type cb_type) enum dock_callback_type cb_type)
{ {
struct acpi_device *adev = dd->adev;
acpi_notify_handler cb = NULL; acpi_notify_handler cb = NULL;
bool run = false; 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); mutex_lock(&hotplug_lock);
if (dd->hp_context) { if (dd->hp_context) {
......
...@@ -60,6 +60,7 @@ static LIST_HEAD(bridge_list); ...@@ -60,6 +60,7 @@ static LIST_HEAD(bridge_list);
static DEFINE_MUTEX(bridge_mutex); static DEFINE_MUTEX(bridge_mutex);
static int acpiphp_hotplug_event(struct acpi_device *adev, u32 type); 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_sanitize_bus(struct pci_bus *bus);
static void acpiphp_set_hpp_values(struct pci_bus *bus); static void acpiphp_set_hpp_values(struct pci_bus *bus);
static void hotplug_event(u32 type, struct acpiphp_context *context); 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) ...@@ -80,7 +81,8 @@ static struct acpiphp_context *acpiphp_init_context(struct acpi_device *adev)
return NULL; return NULL;
context->refcount = 1; 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; return context;
} }
...@@ -130,6 +132,27 @@ static inline void put_bridge(struct acpiphp_bridge *bridge) ...@@ -130,6 +132,27 @@ static inline void put_bridge(struct acpiphp_bridge *bridge)
kref_put(&bridge->ref, free_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) static void free_bridge(struct kref *kref)
{ {
struct acpiphp_context *context; struct acpiphp_context *context;
...@@ -164,28 +187,29 @@ static void free_bridge(struct kref *kref) ...@@ -164,28 +187,29 @@ static void free_bridge(struct kref *kref)
acpi_unlock_hp_context(); acpi_unlock_hp_context();
} }
/* /**
* the _DCK method can do funny things... and sometimes not * acpiphp_post_dock_fixup - Post-dock fixups for PCI devices.
* hah-hah funny. * @adev: ACPI device object corresponding to a PCI device.
* *
* TBD - figure out a way to only call fixups for * TBD - figure out a way to only call fixups for systems that require them.
* 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 acpiphp_context *context = acpiphp_grab_context(adev);
struct pci_bus *bus = context->func.slot->bus; struct pci_bus *bus;
u32 buses; u32 buses;
if (!bus->self) if (!context)
return; return;
bus = context->func.slot->bus;
if (!bus->self)
goto out;
/* fixup bad _DCK function that rewrites /* fixup bad _DCK function that rewrites
* secondary bridge on slot * secondary bridge on slot
*/ */
pci_read_config_dword(bus->self, pci_read_config_dword(bus->self, PCI_PRIMARY_BUS, &buses);
PCI_PRIMARY_BUS,
&buses);
if (((buses >> 8) & 0xff) != bus->busn_res.start) { if (((buses >> 8) & 0xff) != bus->busn_res.start) {
buses = (buses & 0xff000000) buses = (buses & 0xff000000)
...@@ -194,24 +218,11 @@ static void post_dock_fixups(acpi_handle not_used, u32 event, void *data) ...@@ -194,24 +218,11 @@ static void post_dock_fixups(acpi_handle not_used, u32 event, void *data)
| ((unsigned int)(bus->busn_res.end) << 16); | ((unsigned int)(bus->busn_res.end) << 16);
pci_write_config_dword(bus->self, PCI_PRIMARY_BUS, buses); pci_write_config_dword(bus->self, PCI_PRIMARY_BUS, buses);
} }
}
static void dock_event(acpi_handle handle, u32 type, void *data) out:
{ acpiphp_let_context_go(context);
struct acpi_device *adev;
adev = acpi_bus_get_acpi_device(handle);
if (adev) {
acpiphp_hotplug_event(adev, type);
acpi_bus_put_acpi_device(adev);
}
} }
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 */ /* Check whether the PCI device is managed by native PCIe hotplug driver */
static bool device_is_managed_by_native_pciehp(struct pci_dev *pdev) 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) ...@@ -241,20 +252,6 @@ static bool device_is_managed_by_native_pciehp(struct pci_dev *pdev)
return true; 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. * acpiphp_add_context - Add ACPIPHP context to an ACPI device object.
* @handle: ACPI handle of the object to add a context to. * @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, ...@@ -300,15 +297,18 @@ static acpi_status acpiphp_add_context(acpi_handle handle, u32 lvl, void *data,
newfunc = &context->func; newfunc = &context->func;
newfunc->function = function; newfunc->function = function;
newfunc->parent = bridge; 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; newfunc->flags = FUNC_HAS_EJ0;
if (acpi_has_method(handle, "_STA")) if (acpi_has_method(handle, "_STA"))
newfunc->flags |= FUNC_HAS_STA; newfunc->flags |= FUNC_HAS_STA;
acpi_unlock_hp_context();
/* search for objects that share the same slot */ /* search for objects that share the same slot */
list_for_each_entry(slot, &bridge->slots, node) list_for_each_entry(slot, &bridge->slots, node)
if (slot->device == device) if (slot->device == device)
...@@ -369,18 +369,6 @@ static acpi_status acpiphp_add_context(acpi_handle handle, u32 lvl, void *data, ...@@ -369,18 +369,6 @@ static acpi_status acpiphp_add_context(acpi_handle handle, u32 lvl, void *data,
&val, 60*1000)) &val, 60*1000))
slot->flags |= SLOT_ENABLED; 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; return AE_OK;
} }
...@@ -411,11 +399,9 @@ static void cleanup_bridge(struct acpiphp_bridge *bridge) ...@@ -411,11 +399,9 @@ static void cleanup_bridge(struct acpiphp_bridge *bridge)
list_for_each_entry(func, &slot->funcs, sibling) { list_for_each_entry(func, &slot->funcs, sibling) {
struct acpi_device *adev = func_to_acpi_device(func); struct acpi_device *adev = func_to_acpi_device(func);
if (is_dock_device(adev))
unregister_hotplug_dock_device(adev->handle);
acpi_lock_hp_context(); acpi_lock_hp_context();
adev->hp->event = NULL; adev->hp->event = NULL;
adev->hp->fixup = NULL;
acpi_unlock_hp_context(); acpi_unlock_hp_context();
} }
slot->flags |= SLOT_IS_GOING_AWAY; slot->flags |= SLOT_IS_GOING_AWAY;
...@@ -851,19 +837,12 @@ static int acpiphp_hotplug_event(struct acpi_device *adev, u32 type) ...@@ -851,19 +837,12 @@ static int acpiphp_hotplug_event(struct acpi_device *adev, u32 type)
{ {
struct acpiphp_context *context; struct acpiphp_context *context;
acpi_lock_hp_context(); context = acpiphp_grab_context(adev);
context = acpiphp_get_context(adev); if (!context)
if (!context || context->func.parent->is_going_away) {
acpi_unlock_hp_context();
return -ENODATA; return -ENODATA;
}
get_bridge(context->func.parent);
acpiphp_put_context(context);
acpi_unlock_hp_context();
hotplug_event(type, context); hotplug_event(type, context);
acpiphp_let_context_go(context);
put_bridge(context->func.parent);
return 0; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment