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

Merge branch 'acpi-pci-hotplug'

* acpi-pci-hotplug: (34 commits)
  ACPI / PM: Hold acpi_scan_lock over system PM transitions
  ACPI / hotplug / PCI: Fix NULL pointer dereference in cleanup_bridge()
  PCI / ACPI: Use dev_dbg() instead of dev_info() in acpi_pci_set_power_state()
  ACPI / hotplug / PCI: Get rid of check_sub_bridges()
  ACPI / hotplug / PCI: Clean up bridge_mutex usage
  ACPI / hotplug / PCI: Redefine enable_device() and disable_device()
  ACPI / hotplug / PCI: Sanitize acpiphp_get_(latch)|(adapter)_status()
  ACPI / hotplug / PCI: Get rid of unused constants in acpiphp.h
  ACPI / hotplug / PCI: Check for new devices on enabled slots
  ACPI / hotplug / PCI: Allow slots without new devices to be rescanned
  ACPI / hotplug / PCI: Do not check SLOT_ENABLED in enable_device()
  ACPI / hotplug / PCI: Do not exectute _PS0 and _PS3 directly
  ACPI / hotplug / PCI: Do not queue up event handling work items in vain
  ACPI / hotplug / PCI: Consolidate slot disabling and ejecting
  ACPI / hotplug / PCI: Drop redundant checks from check_hotplug_bridge()
  ACPI / hotplug / PCI: Rework namespace scanning and trimming routines
  ACPI / hotplug / PCI: Store parent in functions and bus in slots
  ACPI / hotplug / PCI: Drop handle field from struct acpiphp_bridge
  ACPI / hotplug / PCI: Drop handle field from struct acpiphp_func
  ACPI / hotplug / PCI: Embed function struct into struct acpiphp_context
  ...
parents c92f56cb ad07277e
...@@ -210,6 +210,8 @@ static void pcibios_allocate_bridge_resources(struct pci_dev *dev) ...@@ -210,6 +210,8 @@ static void pcibios_allocate_bridge_resources(struct pci_dev *dev)
r = &dev->resource[idx]; r = &dev->resource[idx];
if (!r->flags) if (!r->flags)
continue; continue;
if (r->parent) /* Already allocated */
continue;
if (!r->start || pci_claim_resource(dev, idx) < 0) { if (!r->start || pci_claim_resource(dev, idx) < 0) {
/* /*
* Something is wrong with the region. * Something is wrong with the region.
...@@ -318,6 +320,8 @@ static void pcibios_allocate_dev_rom_resource(struct pci_dev *dev) ...@@ -318,6 +320,8 @@ static void pcibios_allocate_dev_rom_resource(struct pci_dev *dev)
r = &dev->resource[PCI_ROM_RESOURCE]; r = &dev->resource[PCI_ROM_RESOURCE];
if (!r->flags || !r->start) if (!r->flags || !r->start)
return; return;
if (r->parent) /* Already allocated */
return;
if (pci_claim_resource(dev, PCI_ROM_RESOURCE) < 0) { if (pci_claim_resource(dev, PCI_ROM_RESOURCE) < 0) {
r->end -= r->start; r->end -= r->start;
......
...@@ -159,12 +159,16 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) ...@@ -159,12 +159,16 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv)
return AE_OK; return AE_OK;
} }
void acpi_pci_slot_enumerate(struct pci_bus *bus, acpi_handle handle) void acpi_pci_slot_enumerate(struct pci_bus *bus)
{ {
mutex_lock(&slot_list_lock); acpi_handle handle = ACPI_HANDLE(bus->bridge);
acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1,
register_slot, NULL, bus, NULL); if (handle) {
mutex_unlock(&slot_list_lock); mutex_lock(&slot_list_lock);
acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1,
register_slot, NULL, bus, NULL);
mutex_unlock(&slot_list_lock);
}
} }
void acpi_pci_slot_remove(struct pci_bus *bus) void acpi_pci_slot_remove(struct pci_bus *bus)
......
...@@ -420,10 +420,21 @@ static void acpi_pm_finish(void) ...@@ -420,10 +420,21 @@ static void acpi_pm_finish(void)
} }
/** /**
* acpi_pm_end - Finish up suspend sequence. * acpi_pm_start - Start system PM transition.
*/
static void acpi_pm_start(u32 acpi_state)
{
acpi_target_sleep_state = acpi_state;
acpi_sleep_tts_switch(acpi_target_sleep_state);
acpi_scan_lock_acquire();
}
/**
* acpi_pm_end - Finish up system PM transition.
*/ */
static void acpi_pm_end(void) static void acpi_pm_end(void)
{ {
acpi_scan_lock_release();
/* /*
* This is necessary in case acpi_pm_finish() is not called during a * This is necessary in case acpi_pm_finish() is not called during a
* failing transition to a sleep state. * failing transition to a sleep state.
...@@ -451,21 +462,19 @@ static u32 acpi_suspend_states[] = { ...@@ -451,21 +462,19 @@ static u32 acpi_suspend_states[] = {
static int acpi_suspend_begin(suspend_state_t pm_state) static int acpi_suspend_begin(suspend_state_t pm_state)
{ {
u32 acpi_state = acpi_suspend_states[pm_state]; u32 acpi_state = acpi_suspend_states[pm_state];
int error = 0; int error;
error = (nvs_nosave || nvs_nosave_s3) ? 0 : suspend_nvs_alloc(); error = (nvs_nosave || nvs_nosave_s3) ? 0 : suspend_nvs_alloc();
if (error) if (error)
return error; return error;
if (sleep_states[acpi_state]) { if (!sleep_states[acpi_state]) {
acpi_target_sleep_state = acpi_state; pr_err("ACPI does not support sleep state S%u\n", acpi_state);
acpi_sleep_tts_switch(acpi_target_sleep_state); return -ENOSYS;
} else {
printk(KERN_ERR "ACPI does not support this state: %d\n",
pm_state);
error = -ENOSYS;
} }
return error;
acpi_pm_start(acpi_state);
return 0;
} }
/** /**
...@@ -631,10 +640,8 @@ static int acpi_hibernation_begin(void) ...@@ -631,10 +640,8 @@ static int acpi_hibernation_begin(void)
int error; int error;
error = nvs_nosave ? 0 : suspend_nvs_alloc(); error = nvs_nosave ? 0 : suspend_nvs_alloc();
if (!error) { if (!error)
acpi_target_sleep_state = ACPI_STATE_S4; acpi_pm_start(ACPI_STATE_S4);
acpi_sleep_tts_switch(acpi_target_sleep_state);
}
return error; return error;
} }
...@@ -713,8 +720,10 @@ static int acpi_hibernation_begin_old(void) ...@@ -713,8 +720,10 @@ static int acpi_hibernation_begin_old(void)
if (!error) { if (!error) {
if (!nvs_nosave) if (!nvs_nosave)
error = suspend_nvs_alloc(); error = suspend_nvs_alloc();
if (!error) if (!error) {
acpi_target_sleep_state = ACPI_STATE_S4; acpi_target_sleep_state = ACPI_STATE_S4;
acpi_scan_lock_acquire();
}
} }
return error; return error;
} }
......
...@@ -49,6 +49,7 @@ ...@@ -49,6 +49,7 @@
#define info(format, arg...) printk(KERN_INFO "%s: " format, MY_NAME , ## arg) #define info(format, arg...) printk(KERN_INFO "%s: " format, MY_NAME , ## arg)
#define warn(format, arg...) printk(KERN_WARNING "%s: " format, MY_NAME , ## arg) #define warn(format, arg...) printk(KERN_WARNING "%s: " format, MY_NAME , ## arg)
struct acpiphp_context;
struct acpiphp_bridge; struct acpiphp_bridge;
struct acpiphp_slot; struct acpiphp_slot;
...@@ -59,6 +60,7 @@ struct slot { ...@@ -59,6 +60,7 @@ struct slot {
struct hotplug_slot *hotplug_slot; struct hotplug_slot *hotplug_slot;
struct acpiphp_slot *acpi_slot; struct acpiphp_slot *acpi_slot;
struct hotplug_slot_info info; struct hotplug_slot_info info;
unsigned int sun; /* ACPI _SUN (Slot User Number) value */
}; };
static inline const char *slot_name(struct slot *slot) static inline const char *slot_name(struct slot *slot)
...@@ -75,15 +77,11 @@ struct acpiphp_bridge { ...@@ -75,15 +77,11 @@ struct acpiphp_bridge {
struct list_head list; struct list_head list;
struct list_head slots; struct list_head slots;
struct kref ref; struct kref ref;
acpi_handle handle;
/* Ejectable PCI-to-PCI bridge (PCI bridge and PCI function) */ struct acpiphp_context *context;
struct acpiphp_func *func;
int nr_slots; int nr_slots;
u32 flags;
/* This bus (host bridge) or Secondary bus (PCI-to-PCI bridge) */ /* This bus (host bridge) or Secondary bus (PCI-to-PCI bridge) */
struct pci_bus *pci_bus; struct pci_bus *pci_bus;
...@@ -99,15 +97,13 @@ struct acpiphp_bridge { ...@@ -99,15 +97,13 @@ struct acpiphp_bridge {
*/ */
struct acpiphp_slot { struct acpiphp_slot {
struct list_head node; struct list_head node;
struct acpiphp_bridge *bridge; /* parent */ struct pci_bus *bus;
struct list_head funcs; /* one slot may have different struct list_head funcs; /* one slot may have different
objects (i.e. for each function) */ objects (i.e. for each function) */
struct slot *slot; struct slot *slot;
struct mutex crit_sect; struct mutex crit_sect;
u8 device; /* pci device# */ u8 device; /* pci device# */
unsigned long long sun; /* ACPI _SUN (slot unique number) */
u32 flags; /* see below */ u32 flags; /* see below */
}; };
...@@ -119,15 +115,32 @@ struct acpiphp_slot { ...@@ -119,15 +115,32 @@ struct acpiphp_slot {
* typically 8 objects per slot (i.e. for each PCI function) * typically 8 objects per slot (i.e. for each PCI function)
*/ */
struct acpiphp_func { struct acpiphp_func {
struct acpiphp_slot *slot; /* parent */ struct acpiphp_bridge *parent;
struct acpiphp_slot *slot;
struct list_head sibling; struct list_head sibling;
acpi_handle handle;
u8 function; /* pci function# */ u8 function; /* pci function# */
u32 flags; /* see below */ u32 flags; /* see below */
}; };
struct acpiphp_context {
acpi_handle handle;
struct acpiphp_func func;
struct acpiphp_bridge *bridge;
unsigned int refcount;
};
static inline struct acpiphp_context *func_to_context(struct acpiphp_func *func)
{
return container_of(func, struct acpiphp_context, func);
}
static inline acpi_handle func_to_handle(struct acpiphp_func *func)
{
return func_to_context(func)->handle;
}
/* /*
* struct acpiphp_attention_info - device specific attention registration * struct acpiphp_attention_info - device specific attention registration
* *
...@@ -141,45 +154,32 @@ struct acpiphp_attention_info ...@@ -141,45 +154,32 @@ struct acpiphp_attention_info
struct module *owner; struct module *owner;
}; };
/* PCI bus bridge HID */
#define ACPI_PCI_HOST_HID "PNP0A03"
/* ACPI _STA method value (ignore bit 4; battery present) */ /* ACPI _STA method value (ignore bit 4; battery present) */
#define ACPI_STA_ALL (0x0000000f) #define ACPI_STA_ALL (0x0000000f)
/* bridge flags */
#define BRIDGE_HAS_EJ0 (0x00000001)
/* slot flags */ /* slot flags */
#define SLOT_POWEREDON (0x00000001) #define SLOT_ENABLED (0x00000001)
#define SLOT_ENABLED (0x00000002)
#define SLOT_MULTIFUNCTION (0x00000004)
/* function flags */ /* function flags */
#define FUNC_HAS_STA (0x00000001) #define FUNC_HAS_STA (0x00000001)
#define FUNC_HAS_EJ0 (0x00000002) #define FUNC_HAS_EJ0 (0x00000002)
#define FUNC_HAS_PS0 (0x00000010) #define FUNC_HAS_DCK (0x00000004)
#define FUNC_HAS_PS1 (0x00000020)
#define FUNC_HAS_PS2 (0x00000040)
#define FUNC_HAS_PS3 (0x00000080)
#define FUNC_HAS_DCK (0x00000100)
/* function prototypes */ /* function prototypes */
/* acpiphp_core.c */ /* acpiphp_core.c */
int acpiphp_register_attention(struct acpiphp_attention_info*info); int acpiphp_register_attention(struct acpiphp_attention_info*info);
int acpiphp_unregister_attention(struct acpiphp_attention_info *info); int acpiphp_unregister_attention(struct acpiphp_attention_info *info);
int acpiphp_register_hotplug_slot(struct acpiphp_slot *slot); int acpiphp_register_hotplug_slot(struct acpiphp_slot *slot, unsigned int sun);
void acpiphp_unregister_hotplug_slot(struct acpiphp_slot *slot); void acpiphp_unregister_hotplug_slot(struct acpiphp_slot *slot);
/* acpiphp_glue.c */ /* acpiphp_glue.c */
typedef int (*acpiphp_callback)(struct acpiphp_slot *slot, void *data); typedef int (*acpiphp_callback)(struct acpiphp_slot *slot, void *data);
int acpiphp_enable_slot(struct acpiphp_slot *slot); int acpiphp_enable_slot(struct acpiphp_slot *slot);
int acpiphp_disable_slot(struct acpiphp_slot *slot); int acpiphp_disable_and_eject_slot(struct acpiphp_slot *slot);
int acpiphp_eject_slot(struct acpiphp_slot *slot);
u8 acpiphp_get_power_status(struct acpiphp_slot *slot); u8 acpiphp_get_power_status(struct acpiphp_slot *slot);
u8 acpiphp_get_attention_status(struct acpiphp_slot *slot); u8 acpiphp_get_attention_status(struct acpiphp_slot *slot);
u8 acpiphp_get_latch_status(struct acpiphp_slot *slot); u8 acpiphp_get_latch_status(struct acpiphp_slot *slot);
......
...@@ -155,15 +155,11 @@ static int enable_slot(struct hotplug_slot *hotplug_slot) ...@@ -155,15 +155,11 @@ static int enable_slot(struct hotplug_slot *hotplug_slot)
static int disable_slot(struct hotplug_slot *hotplug_slot) static int disable_slot(struct hotplug_slot *hotplug_slot)
{ {
struct slot *slot = hotplug_slot->private; struct slot *slot = hotplug_slot->private;
int retval;
dbg("%s - physical_slot = %s\n", __func__, slot_name(slot)); dbg("%s - physical_slot = %s\n", __func__, slot_name(slot));
/* disable the specified slot */ /* disable the specified slot */
retval = acpiphp_disable_slot(slot->acpi_slot); return acpiphp_disable_and_eject_slot(slot->acpi_slot);
if (!retval)
retval = acpiphp_eject_slot(slot->acpi_slot);
return retval;
} }
...@@ -290,7 +286,8 @@ static void release_slot(struct hotplug_slot *hotplug_slot) ...@@ -290,7 +286,8 @@ static void release_slot(struct hotplug_slot *hotplug_slot)
} }
/* callback routine to initialize 'struct slot' for each slot */ /* callback routine to initialize 'struct slot' for each slot */
int acpiphp_register_hotplug_slot(struct acpiphp_slot *acpiphp_slot) int acpiphp_register_hotplug_slot(struct acpiphp_slot *acpiphp_slot,
unsigned int sun)
{ {
struct slot *slot; struct slot *slot;
int retval = -ENOMEM; int retval = -ENOMEM;
...@@ -317,12 +314,11 @@ int acpiphp_register_hotplug_slot(struct acpiphp_slot *acpiphp_slot) ...@@ -317,12 +314,11 @@ int acpiphp_register_hotplug_slot(struct acpiphp_slot *acpiphp_slot)
slot->hotplug_slot->info->adapter_status = acpiphp_get_adapter_status(slot->acpi_slot); slot->hotplug_slot->info->adapter_status = acpiphp_get_adapter_status(slot->acpi_slot);
acpiphp_slot->slot = slot; acpiphp_slot->slot = slot;
snprintf(name, SLOT_NAME_SIZE, "%llu", slot->acpi_slot->sun); slot->sun = sun;
snprintf(name, SLOT_NAME_SIZE, "%u", sun);
retval = pci_hp_register(slot->hotplug_slot, retval = pci_hp_register(slot->hotplug_slot, acpiphp_slot->bus,
acpiphp_slot->bridge->pci_bus, acpiphp_slot->device, name);
acpiphp_slot->device,
name);
if (retval == -EBUSY) if (retval == -EBUSY)
goto error_hpslot; goto error_hpslot;
if (retval) { if (retval) {
......
...@@ -46,6 +46,7 @@ ...@@ -46,6 +46,7 @@
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/pci_hotplug.h> #include <linux/pci_hotplug.h>
#include <linux/pci-acpi.h> #include <linux/pci-acpi.h>
#include <linux/pm_runtime.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/acpi.h> #include <linux/acpi.h>
...@@ -55,28 +56,82 @@ ...@@ -55,28 +56,82 @@
static LIST_HEAD(bridge_list); static LIST_HEAD(bridge_list);
static DEFINE_MUTEX(bridge_mutex); static DEFINE_MUTEX(bridge_mutex);
static DEFINE_MUTEX(acpiphp_context_lock);
#define MY_NAME "acpiphp_glue" #define MY_NAME "acpiphp_glue"
static void handle_hotplug_event_bridge (acpi_handle, u32, void *); static void handle_hotplug_event(acpi_handle handle, u32 type, void *data);
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_func(acpi_handle handle, u32 type, void *context); static void hotplug_event(acpi_handle handle, u32 type, void *data);
static void handle_hotplug_event_func(acpi_handle handle, u32 type, void *context);
static void free_bridge(struct kref *kref); static void free_bridge(struct kref *kref);
/* callback routine to check for the existence of a pci dock device */ static void acpiphp_context_handler(acpi_handle handle, void *context)
static acpi_status
is_pci_dock_device(acpi_handle handle, u32 lvl, void *context, void **rv)
{ {
int *count = (int *)context; /* Intentionally empty. */
}
if (is_dock_device(handle)) { /**
(*count)++; * acpiphp_init_context - Create hotplug context and grab a reference to it.
return AE_CTRL_TERMINATE; * @handle: ACPI object handle to create the context for.
} else { *
return AE_OK; * Call under acpiphp_context_lock.
*/
static struct acpiphp_context *acpiphp_init_context(acpi_handle handle)
{
struct acpiphp_context *context;
acpi_status status;
context = kzalloc(sizeof(*context), GFP_KERNEL);
if (!context)
return NULL;
context->handle = handle;
context->refcount = 1;
status = acpi_attach_data(handle, acpiphp_context_handler, context);
if (ACPI_FAILURE(status)) {
kfree(context);
return NULL;
} }
return context;
}
/**
* acpiphp_get_context - Get hotplug context and grab a reference to it.
* @handle: ACPI object handle to get the context for.
*
* Call under acpiphp_context_lock.
*/
static struct acpiphp_context *acpiphp_get_context(acpi_handle handle)
{
struct acpiphp_context *context = NULL;
acpi_status status;
void *data;
status = acpi_get_data(handle, acpiphp_context_handler, &data);
if (ACPI_SUCCESS(status)) {
context = data;
context->refcount++;
}
return context;
}
/**
* acpiphp_put_context - Drop a reference to ACPI hotplug context.
* @handle: ACPI object handle to put the context for.
*
* The context object is removed if there are no more references to it.
*
* Call under acpiphp_context_lock.
*/
static void acpiphp_put_context(struct acpiphp_context *context)
{
if (--context->refcount)
return;
WARN_ON(context->bridge);
acpi_detach_data(context->handle, acpiphp_context_handler);
kfree(context);
} }
static inline void get_bridge(struct acpiphp_bridge *bridge) static inline void get_bridge(struct acpiphp_bridge *bridge)
...@@ -91,25 +146,36 @@ static inline void put_bridge(struct acpiphp_bridge *bridge) ...@@ -91,25 +146,36 @@ static inline void put_bridge(struct acpiphp_bridge *bridge)
static void free_bridge(struct kref *kref) static void free_bridge(struct kref *kref)
{ {
struct acpiphp_context *context;
struct acpiphp_bridge *bridge; struct acpiphp_bridge *bridge;
struct acpiphp_slot *slot, *next; struct acpiphp_slot *slot, *next;
struct acpiphp_func *func, *tmp; struct acpiphp_func *func, *tmp;
mutex_lock(&acpiphp_context_lock);
bridge = container_of(kref, struct acpiphp_bridge, ref); bridge = container_of(kref, struct acpiphp_bridge, ref);
list_for_each_entry_safe(slot, next, &bridge->slots, node) { list_for_each_entry_safe(slot, next, &bridge->slots, node) {
list_for_each_entry_safe(func, tmp, &slot->funcs, sibling) { list_for_each_entry_safe(func, tmp, &slot->funcs, sibling)
kfree(func); acpiphp_put_context(func_to_context(func));
}
kfree(slot); kfree(slot);
} }
/* Release reference acquired by acpiphp_bridge_handle_to_function() */ context = bridge->context;
if ((bridge->flags & BRIDGE_HAS_EJ0) && bridge->func) /* Root bridges will not have hotplug context. */
put_bridge(bridge->func->slot->bridge); if (context) {
/* Release the reference taken by acpiphp_enumerate_slots(). */
put_bridge(context->func.parent);
context->bridge = NULL;
acpiphp_put_context(context);
}
put_device(&bridge->pci_bus->dev); put_device(&bridge->pci_bus->dev);
pci_dev_put(bridge->pci_dev); pci_dev_put(bridge->pci_dev);
kfree(bridge); kfree(bridge);
mutex_unlock(&acpiphp_context_lock);
} }
/* /*
...@@ -121,8 +187,8 @@ static void free_bridge(struct kref *kref) ...@@ -121,8 +187,8 @@ static void free_bridge(struct kref *kref)
*/ */
static void post_dock_fixups(acpi_handle not_used, u32 event, void *data) static void post_dock_fixups(acpi_handle not_used, u32 event, void *data)
{ {
struct acpiphp_func *func = data; struct acpiphp_context *context = data;
struct pci_bus *bus = func->slot->bridge->pci_bus; struct pci_bus *bus = context->func.slot->bus;
u32 buses; u32 buses;
if (!bus->self) if (!bus->self)
...@@ -147,7 +213,7 @@ static void post_dock_fixups(acpi_handle not_used, u32 event, void *data) ...@@ -147,7 +213,7 @@ static void post_dock_fixups(acpi_handle not_used, u32 event, void *data)
static const struct acpi_dock_ops acpiphp_dock_ops = { static const struct acpi_dock_ops acpiphp_dock_ops = {
.fixup = post_dock_fixups, .fixup = post_dock_fixups,
.handler = hotplug_event_func, .handler = hotplug_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 */
...@@ -181,54 +247,56 @@ static bool device_is_managed_by_native_pciehp(struct pci_dev *pdev) ...@@ -181,54 +247,56 @@ static bool device_is_managed_by_native_pciehp(struct pci_dev *pdev)
static void acpiphp_dock_init(void *data) static void acpiphp_dock_init(void *data)
{ {
struct acpiphp_func *func = data; struct acpiphp_context *context = data;
get_bridge(func->slot->bridge); get_bridge(context->func.parent);
} }
static void acpiphp_dock_release(void *data) static void acpiphp_dock_release(void *data)
{ {
struct acpiphp_func *func = data; struct acpiphp_context *context = data;
put_bridge(func->slot->bridge); put_bridge(context->func.parent);
} }
/* callback routine to register each ACPI PCI slot object */ /* callback routine to register each ACPI PCI slot object */
static acpi_status static acpi_status register_slot(acpi_handle handle, u32 lvl, void *data,
register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) void **rv)
{ {
struct acpiphp_bridge *bridge = (struct acpiphp_bridge *)context; struct acpiphp_bridge *bridge = data;
struct acpiphp_context *context;
struct acpiphp_slot *slot; struct acpiphp_slot *slot;
struct acpiphp_func *newfunc; struct acpiphp_func *newfunc;
acpi_status status = AE_OK; acpi_status status = AE_OK;
unsigned long long adr, sun; unsigned long long adr;
int device, function, retval, found = 0; int device, function;
struct pci_bus *pbus = bridge->pci_bus; struct pci_bus *pbus = bridge->pci_bus;
struct pci_dev *pdev; struct pci_dev *pdev = bridge->pci_dev;
u32 val; u32 val;
if (!acpi_pci_check_ejectable(pbus, handle) && !is_dock_device(handle)) if (pdev && device_is_managed_by_native_pciehp(pdev))
return AE_OK; return AE_OK;
status = acpi_evaluate_integer(handle, "_ADR", NULL, &adr); status = acpi_evaluate_integer(handle, "_ADR", NULL, &adr);
if (ACPI_FAILURE(status)) { if (ACPI_FAILURE(status)) {
warn("can't evaluate _ADR (%#x)\n", status); acpi_handle_warn(handle, "can't evaluate _ADR (%#x)\n", status);
return AE_OK; return AE_OK;
} }
device = (adr >> 16) & 0xffff; device = (adr >> 16) & 0xffff;
function = adr & 0xffff; function = adr & 0xffff;
pdev = bridge->pci_dev; mutex_lock(&acpiphp_context_lock);
if (pdev && device_is_managed_by_native_pciehp(pdev)) context = acpiphp_init_context(handle);
return AE_OK; if (!context) {
mutex_unlock(&acpiphp_context_lock);
newfunc = kzalloc(sizeof(struct acpiphp_func), GFP_KERNEL); acpi_handle_err(handle, "No hotplug context\n");
if (!newfunc) return AE_NOT_EXIST;
return AE_NO_MEMORY; }
newfunc = &context->func;
newfunc->handle = handle;
newfunc->function = function; newfunc->function = function;
newfunc->parent = bridge;
mutex_unlock(&acpiphp_context_lock);
if (acpi_has_method(handle, "_EJ0")) if (acpi_has_method(handle, "_EJ0"))
newfunc->flags = FUNC_HAS_EJ0; newfunc->flags = FUNC_HAS_EJ0;
...@@ -236,73 +304,61 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) ...@@ -236,73 +304,61 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv)
if (acpi_has_method(handle, "_STA")) if (acpi_has_method(handle, "_STA"))
newfunc->flags |= FUNC_HAS_STA; newfunc->flags |= FUNC_HAS_STA;
if (acpi_has_method(handle, "_PS0"))
newfunc->flags |= FUNC_HAS_PS0;
if (acpi_has_method(handle, "_PS3"))
newfunc->flags |= FUNC_HAS_PS3;
if (acpi_has_method(handle, "_DCK")) if (acpi_has_method(handle, "_DCK"))
newfunc->flags |= FUNC_HAS_DCK; newfunc->flags |= FUNC_HAS_DCK;
status = acpi_evaluate_integer(handle, "_SUN", NULL, &sun);
if (ACPI_FAILURE(status)) {
/*
* use the count of the number of slots we've found
* for the number of the slot
*/
sun = bridge->nr_slots+1;
}
/* 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)
if (slot->sun != sun) goto slot_found;
warn("sibling found, but _SUN doesn't match!\n");
found = 1;
break;
}
if (!found) { slot = kzalloc(sizeof(struct acpiphp_slot), GFP_KERNEL);
slot = kzalloc(sizeof(struct acpiphp_slot), GFP_KERNEL); if (!slot) {
if (!slot) { status = AE_NO_MEMORY;
kfree(newfunc); goto err;
return AE_NO_MEMORY; }
}
slot->bus = bridge->pci_bus;
slot->device = device;
INIT_LIST_HEAD(&slot->funcs);
mutex_init(&slot->crit_sect);
list_add_tail(&slot->node, &bridge->slots);
slot->bridge = bridge; /* Register slots for ejectable funtions only. */
slot->device = device; if (acpi_pci_check_ejectable(pbus, handle) || is_dock_device(handle)) {
slot->sun = sun; unsigned long long sun;
INIT_LIST_HEAD(&slot->funcs); int retval;
mutex_init(&slot->crit_sect);
mutex_lock(&bridge_mutex);
list_add_tail(&slot->node, &bridge->slots);
mutex_unlock(&bridge_mutex);
bridge->nr_slots++; bridge->nr_slots++;
status = acpi_evaluate_integer(handle, "_SUN", NULL, &sun);
if (ACPI_FAILURE(status))
sun = bridge->nr_slots;
dbg("found ACPI PCI Hotplug slot %llu at PCI %04x:%02x:%02x\n", dbg("found ACPI PCI Hotplug slot %llu at PCI %04x:%02x:%02x\n",
slot->sun, pci_domain_nr(pbus), pbus->number, device); sun, pci_domain_nr(pbus), pbus->number, device);
retval = acpiphp_register_hotplug_slot(slot);
retval = acpiphp_register_hotplug_slot(slot, sun);
if (retval) { if (retval) {
slot->slot = NULL;
bridge->nr_slots--;
if (retval == -EBUSY) if (retval == -EBUSY)
warn("Slot %llu already registered by another " warn("Slot %llu already registered by another "
"hotplug driver\n", slot->sun); "hotplug driver\n", sun);
else else
warn("acpiphp_register_hotplug_slot failed " warn("acpiphp_register_hotplug_slot failed "
"(err code = 0x%x)\n", retval); "(err code = 0x%x)\n", retval);
goto err_exit;
} }
/* Even if the slot registration fails, we can still use it. */
} }
slot_found:
newfunc->slot = slot; newfunc->slot = slot;
mutex_lock(&bridge_mutex);
list_add_tail(&newfunc->sibling, &slot->funcs); list_add_tail(&newfunc->sibling, &slot->funcs);
mutex_unlock(&bridge_mutex);
if (pci_bus_read_dev_vendor_id(pbus, PCI_DEVFN(device, function), if (pci_bus_read_dev_vendor_id(pbus, PCI_DEVFN(device, function),
&val, 60*1000)) &val, 60*1000))
slot->flags |= (SLOT_ENABLED | SLOT_POWEREDON); slot->flags |= SLOT_ENABLED;
if (is_dock_device(handle)) { if (is_dock_device(handle)) {
/* we don't want to call this device's _EJ0 /* we don't want to call this device's _EJ0
...@@ -311,128 +367,46 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) ...@@ -311,128 +367,46 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv)
*/ */
newfunc->flags &= ~FUNC_HAS_EJ0; newfunc->flags &= ~FUNC_HAS_EJ0;
if (register_hotplug_dock_device(handle, if (register_hotplug_dock_device(handle,
&acpiphp_dock_ops, newfunc, &acpiphp_dock_ops, context,
acpiphp_dock_init, acpiphp_dock_release)) acpiphp_dock_init, acpiphp_dock_release))
dbg("failed to register dock device\n"); dbg("failed to register dock device\n");
} }
/* install notify handler */ /* install notify handler */
if (!(newfunc->flags & FUNC_HAS_DCK)) { if (!(newfunc->flags & FUNC_HAS_DCK)) {
status = acpi_install_notify_handler(handle, status = acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
ACPI_SYSTEM_NOTIFY, handle_hotplug_event,
handle_hotplug_event_func, context);
newfunc);
if (ACPI_FAILURE(status)) if (ACPI_FAILURE(status))
err("failed to register interrupt notify handler\n"); acpi_handle_err(handle,
} else "failed to install notify handler\n");
status = AE_OK;
return status;
err_exit:
bridge->nr_slots--;
mutex_lock(&bridge_mutex);
list_del(&slot->node);
mutex_unlock(&bridge_mutex);
kfree(slot);
kfree(newfunc);
return AE_OK;
}
/* see if it's worth looking at this bridge */
static int detect_ejectable_slots(acpi_handle handle)
{
int found = acpi_pci_detect_ejectable(handle);
if (!found) {
acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1,
is_pci_dock_device, NULL, (void *)&found, NULL);
}
return found;
}
/* initialize miscellaneous stuff for both root and PCI-to-PCI bridge */
static void init_bridge_misc(struct acpiphp_bridge *bridge)
{
acpi_status status;
/* must be added to the list prior to calling register_slot */
mutex_lock(&bridge_mutex);
list_add(&bridge->list, &bridge_list);
mutex_unlock(&bridge_mutex);
/* register all slot objects under this bridge */
status = acpi_walk_namespace(ACPI_TYPE_DEVICE, bridge->handle, (u32)1,
register_slot, NULL, bridge, NULL);
if (ACPI_FAILURE(status)) {
mutex_lock(&bridge_mutex);
list_del(&bridge->list);
mutex_unlock(&bridge_mutex);
return;
}
/* install notify handler for P2P bridges */
if (!pci_is_root_bus(bridge->pci_bus)) {
if ((bridge->flags & BRIDGE_HAS_EJ0) && bridge->func) {
status = acpi_remove_notify_handler(bridge->func->handle,
ACPI_SYSTEM_NOTIFY,
handle_hotplug_event_func);
if (ACPI_FAILURE(status))
err("failed to remove notify handler\n");
}
status = acpi_install_notify_handler(bridge->handle,
ACPI_SYSTEM_NOTIFY,
handle_hotplug_event_bridge,
bridge);
if (ACPI_FAILURE(status)) {
err("failed to register interrupt notify handler\n");
}
} }
}
/* find acpiphp_func from acpiphp_bridge */
static struct acpiphp_func *acpiphp_bridge_handle_to_function(acpi_handle handle)
{
struct acpiphp_bridge *bridge;
struct acpiphp_slot *slot;
struct acpiphp_func *func = NULL;
mutex_lock(&bridge_mutex); return AE_OK;
list_for_each_entry(bridge, &bridge_list, list) {
list_for_each_entry(slot, &bridge->slots, node) {
list_for_each_entry(func, &slot->funcs, sibling) {
if (func->handle == handle) {
get_bridge(func->slot->bridge);
mutex_unlock(&bridge_mutex);
return func;
}
}
}
}
mutex_unlock(&bridge_mutex);
return NULL; err:
mutex_lock(&acpiphp_context_lock);
acpiphp_put_context(context);
mutex_unlock(&acpiphp_context_lock);
return status;
} }
static struct acpiphp_bridge *acpiphp_handle_to_bridge(acpi_handle handle) static struct acpiphp_bridge *acpiphp_handle_to_bridge(acpi_handle handle)
{ {
struct acpiphp_bridge *bridge; struct acpiphp_context *context;
struct acpiphp_bridge *bridge = NULL;
mutex_lock(&bridge_mutex);
list_for_each_entry(bridge, &bridge_list, list) mutex_lock(&acpiphp_context_lock);
if (bridge->handle == handle) { context = acpiphp_get_context(handle);
if (context) {
bridge = context->bridge;
if (bridge)
get_bridge(bridge); get_bridge(bridge);
mutex_unlock(&bridge_mutex);
return bridge;
}
mutex_unlock(&bridge_mutex);
return NULL; acpiphp_put_context(context);
}
mutex_unlock(&acpiphp_context_lock);
return bridge;
} }
static void cleanup_bridge(struct acpiphp_bridge *bridge) static void cleanup_bridge(struct acpiphp_bridge *bridge)
...@@ -440,39 +414,24 @@ static void cleanup_bridge(struct acpiphp_bridge *bridge) ...@@ -440,39 +414,24 @@ static void cleanup_bridge(struct acpiphp_bridge *bridge)
struct acpiphp_slot *slot; struct acpiphp_slot *slot;
struct acpiphp_func *func; struct acpiphp_func *func;
acpi_status status; acpi_status status;
acpi_handle handle = bridge->handle;
if (!pci_is_root_bus(bridge->pci_bus)) {
status = acpi_remove_notify_handler(handle,
ACPI_SYSTEM_NOTIFY,
handle_hotplug_event_bridge);
if (ACPI_FAILURE(status))
err("failed to remove notify handler\n");
}
if ((bridge->flags & BRIDGE_HAS_EJ0) && bridge->func) {
status = acpi_install_notify_handler(bridge->func->handle,
ACPI_SYSTEM_NOTIFY,
handle_hotplug_event_func,
bridge->func);
if (ACPI_FAILURE(status))
err("failed to install interrupt notify handler\n");
}
list_for_each_entry(slot, &bridge->slots, node) { list_for_each_entry(slot, &bridge->slots, node) {
list_for_each_entry(func, &slot->funcs, sibling) { list_for_each_entry(func, &slot->funcs, sibling) {
if (is_dock_device(func->handle)) { acpi_handle handle = func_to_handle(func);
unregister_hotplug_dock_device(func->handle);
} if (is_dock_device(handle))
unregister_hotplug_dock_device(handle);
if (!(func->flags & FUNC_HAS_DCK)) { if (!(func->flags & FUNC_HAS_DCK)) {
status = acpi_remove_notify_handler(func->handle, status = acpi_remove_notify_handler(handle,
ACPI_SYSTEM_NOTIFY, ACPI_SYSTEM_NOTIFY,
handle_hotplug_event_func); handle_hotplug_event);
if (ACPI_FAILURE(status)) if (ACPI_FAILURE(status))
err("failed to remove notify handler\n"); err("failed to remove notify handler\n");
} }
} }
acpiphp_unregister_hotplug_slot(slot); if (slot->slot)
acpiphp_unregister_hotplug_slot(slot);
} }
mutex_lock(&bridge_mutex); mutex_lock(&bridge_mutex);
...@@ -480,71 +439,6 @@ static void cleanup_bridge(struct acpiphp_bridge *bridge) ...@@ -480,71 +439,6 @@ static void cleanup_bridge(struct acpiphp_bridge *bridge)
mutex_unlock(&bridge_mutex); mutex_unlock(&bridge_mutex);
} }
static int power_on_slot(struct acpiphp_slot *slot)
{
acpi_status status;
struct acpiphp_func *func;
int retval = 0;
/* if already enabled, just skip */
if (slot->flags & SLOT_POWEREDON)
goto err_exit;
list_for_each_entry(func, &slot->funcs, sibling) {
if (func->flags & FUNC_HAS_PS0) {
dbg("%s: executing _PS0\n", __func__);
status = acpi_evaluate_object(func->handle, "_PS0", NULL, NULL);
if (ACPI_FAILURE(status)) {
warn("%s: _PS0 failed\n", __func__);
retval = -1;
goto err_exit;
} else
break;
}
}
/* TBD: evaluate _STA to check if the slot is enabled */
slot->flags |= SLOT_POWEREDON;
err_exit:
return retval;
}
static int power_off_slot(struct acpiphp_slot *slot)
{
acpi_status status;
struct acpiphp_func *func;
int retval = 0;
/* if already disabled, just skip */
if ((slot->flags & SLOT_POWEREDON) == 0)
goto err_exit;
list_for_each_entry(func, &slot->funcs, sibling) {
if (func->flags & FUNC_HAS_PS3) {
status = acpi_evaluate_object(func->handle, "_PS3", NULL, NULL);
if (ACPI_FAILURE(status)) {
warn("%s: _PS3 failed\n", __func__);
retval = -1;
goto err_exit;
} else
break;
}
}
/* TBD: evaluate _STA to check if the slot is disabled */
slot->flags &= (~SLOT_POWEREDON);
err_exit:
return retval;
}
/** /**
* acpiphp_max_busnr - return the highest reserved bus number under the given bus. * acpiphp_max_busnr - return the highest reserved bus number under the given bus.
* @bus: bus to start search with * @bus: bus to start search with
...@@ -572,52 +466,32 @@ static unsigned char acpiphp_max_busnr(struct pci_bus *bus) ...@@ -572,52 +466,32 @@ static unsigned char acpiphp_max_busnr(struct pci_bus *bus)
return max; return max;
} }
/** /**
* acpiphp_bus_add - add a new bus to acpi subsystem * acpiphp_bus_trim - Trim device objects in an ACPI namespace subtree.
* @func: acpiphp_func of the bridge * @handle: ACPI device object handle to start from.
*/ */
static int acpiphp_bus_add(struct acpiphp_func *func) static void acpiphp_bus_trim(acpi_handle handle)
{ {
struct acpi_device *device; struct acpi_device *adev = NULL;
int ret_val;
if (!acpi_bus_get_device(func->handle, &device)) { acpi_bus_get_device(handle, &adev);
dbg("bus exists... trim\n"); if (adev)
/* this shouldn't be in here, so remove acpi_bus_trim(adev);
* the bus then re-add it...
*/
acpi_bus_trim(device);
}
ret_val = acpi_bus_scan(func->handle);
if (!ret_val)
ret_val = acpi_bus_get_device(func->handle, &device);
if (ret_val)
dbg("error adding bus, %x\n", -ret_val);
return ret_val;
} }
/** /**
* acpiphp_bus_trim - trim a bus from acpi subsystem * acpiphp_bus_add - Scan ACPI namespace subtree.
* @handle: handle to acpi namespace * @handle: ACPI object handle to start the scan from.
*/ */
static int acpiphp_bus_trim(acpi_handle handle) static void acpiphp_bus_add(acpi_handle handle)
{ {
struct acpi_device *device; struct acpi_device *adev = NULL;
int retval;
retval = acpi_bus_get_device(handle, &device);
if (retval) {
dbg("acpi_device not found\n");
return retval;
}
acpi_bus_trim(device); acpiphp_bus_trim(handle);
return 0; acpi_bus_scan(handle);
acpi_bus_get_device(handle, &adev);
if (adev)
acpi_device_set_power(adev, ACPI_STATE_D0);
} }
static void acpiphp_set_acpi_region(struct acpiphp_slot *slot) static void acpiphp_set_acpi_region(struct acpiphp_slot *slot)
...@@ -634,7 +508,8 @@ static void acpiphp_set_acpi_region(struct acpiphp_slot *slot) ...@@ -634,7 +508,8 @@ static void acpiphp_set_acpi_region(struct acpiphp_slot *slot)
params[1].type = ACPI_TYPE_INTEGER; params[1].type = ACPI_TYPE_INTEGER;
params[1].integer.value = 1; params[1].integer.value = 1;
/* _REG is optional, we don't care about if there is failure */ /* _REG is optional, we don't care about if there is failure */
acpi_evaluate_object(func->handle, "_REG", &arg_list, NULL); acpi_evaluate_object(func_to_handle(func), "_REG", &arg_list,
NULL);
} }
} }
...@@ -642,59 +517,44 @@ static void check_hotplug_bridge(struct acpiphp_slot *slot, struct pci_dev *dev) ...@@ -642,59 +517,44 @@ static void check_hotplug_bridge(struct acpiphp_slot *slot, struct pci_dev *dev)
{ {
struct acpiphp_func *func; struct acpiphp_func *func;
if (!dev->subordinate)
return;
/* quirk, or pcie could set it already */ /* quirk, or pcie could set it already */
if (dev->is_hotplug_bridge) if (dev->is_hotplug_bridge)
return; return;
if (PCI_SLOT(dev->devfn) != slot->device)
return;
list_for_each_entry(func, &slot->funcs, sibling) { list_for_each_entry(func, &slot->funcs, sibling) {
if (PCI_FUNC(dev->devfn) == func->function) { if (PCI_FUNC(dev->devfn) == func->function) {
/* check if this bridge has ejectable slots */ dev->is_hotplug_bridge = 1;
if ((detect_ejectable_slots(func->handle) > 0))
dev->is_hotplug_bridge = 1;
break; break;
} }
} }
} }
/** /**
* enable_device - enable, configure a slot * enable_slot - enable, configure a slot
* @slot: slot to be enabled * @slot: slot to be enabled
* *
* This function should be called per *physical slot*, * This function should be called per *physical slot*,
* not per each slot object in ACPI namespace. * not per each slot object in ACPI namespace.
*/ */
static int __ref enable_device(struct acpiphp_slot *slot) static void __ref enable_slot(struct acpiphp_slot *slot)
{ {
struct pci_dev *dev; struct pci_dev *dev;
struct pci_bus *bus = slot->bridge->pci_bus; struct pci_bus *bus = slot->bus;
struct acpiphp_func *func; struct acpiphp_func *func;
int num, max, pass; int max, pass;
LIST_HEAD(add_list); LIST_HEAD(add_list);
if (slot->flags & SLOT_ENABLED)
goto err_exit;
list_for_each_entry(func, &slot->funcs, sibling) list_for_each_entry(func, &slot->funcs, sibling)
acpiphp_bus_add(func); acpiphp_bus_add(func_to_handle(func));
num = pci_scan_slot(bus, PCI_DEVFN(slot->device, 0)); pci_scan_slot(bus, PCI_DEVFN(slot->device, 0));
if (num == 0) {
/* Maybe only part of funcs are added. */
dbg("No new device found\n");
goto err_exit;
}
max = acpiphp_max_busnr(bus); max = acpiphp_max_busnr(bus);
for (pass = 0; pass < 2; pass++) { for (pass = 0; pass < 2; pass++) {
list_for_each_entry(dev, &bus->devices, bus_list) { list_for_each_entry(dev, &bus->devices, bus_list) {
if (PCI_SLOT(dev->devfn) != slot->device) if (PCI_SLOT(dev->devfn) != slot->device)
continue; continue;
if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE || if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE ||
dev->hdr_type == PCI_HEADER_TYPE_CARDBUS) { dev->hdr_type == PCI_HEADER_TYPE_CARDBUS) {
max = pci_scan_bridge(bus, dev, max, pass); max = pci_scan_bridge(bus, dev, max, pass);
...@@ -733,16 +593,12 @@ static int __ref enable_device(struct acpiphp_slot *slot) ...@@ -733,16 +593,12 @@ static int __ref enable_device(struct acpiphp_slot *slot)
continue; continue;
} }
} }
err_exit:
return 0;
} }
/* return first device in slot, acquiring a reference on it */ /* return first device in slot, acquiring a reference on it */
static struct pci_dev *dev_in_slot(struct acpiphp_slot *slot) static struct pci_dev *dev_in_slot(struct acpiphp_slot *slot)
{ {
struct pci_bus *bus = slot->bridge->pci_bus; struct pci_bus *bus = slot->bus;
struct pci_dev *dev; struct pci_dev *dev;
struct pci_dev *ret = NULL; struct pci_dev *ret = NULL;
...@@ -758,16 +614,16 @@ static struct pci_dev *dev_in_slot(struct acpiphp_slot *slot) ...@@ -758,16 +614,16 @@ static struct pci_dev *dev_in_slot(struct acpiphp_slot *slot)
} }
/** /**
* disable_device - disable a slot * disable_slot - disable a slot
* @slot: ACPI PHP slot * @slot: ACPI PHP slot
*/ */
static int disable_device(struct acpiphp_slot *slot) static void disable_slot(struct acpiphp_slot *slot)
{ {
struct acpiphp_func *func; struct acpiphp_func *func;
struct pci_dev *pdev; struct pci_dev *pdev;
/* /*
* enable_device() enumerates all functions in this device via * enable_slot() enumerates all functions in this device via
* pci_scan_slot(), whether they have associated ACPI hotplug * pci_scan_slot(), whether they have associated ACPI hotplug
* methods (_EJ0, etc.) or not. Therefore, we remove all functions * methods (_EJ0, etc.) or not. Therefore, we remove all functions
* here. * here.
...@@ -777,13 +633,10 @@ static int disable_device(struct acpiphp_slot *slot) ...@@ -777,13 +633,10 @@ static int disable_device(struct acpiphp_slot *slot)
pci_dev_put(pdev); pci_dev_put(pdev);
} }
list_for_each_entry(func, &slot->funcs, sibling) { list_for_each_entry(func, &slot->funcs, sibling)
acpiphp_bus_trim(func->handle); acpiphp_bus_trim(func_to_handle(func));
}
slot->flags &= (~SLOT_ENABLED); slot->flags &= (~SLOT_ENABLED);
return 0;
} }
...@@ -801,18 +654,21 @@ static int disable_device(struct acpiphp_slot *slot) ...@@ -801,18 +654,21 @@ static int disable_device(struct acpiphp_slot *slot)
*/ */
static unsigned int get_slot_status(struct acpiphp_slot *slot) static unsigned int get_slot_status(struct acpiphp_slot *slot)
{ {
acpi_status status;
unsigned long long sta = 0; unsigned long long sta = 0;
u32 dvid;
struct acpiphp_func *func; struct acpiphp_func *func;
list_for_each_entry(func, &slot->funcs, sibling) { list_for_each_entry(func, &slot->funcs, sibling) {
if (func->flags & FUNC_HAS_STA) { if (func->flags & FUNC_HAS_STA) {
status = acpi_evaluate_integer(func->handle, "_STA", NULL, &sta); acpi_status status;
status = acpi_evaluate_integer(func_to_handle(func),
"_STA", NULL, &sta);
if (ACPI_SUCCESS(status) && sta) if (ACPI_SUCCESS(status) && sta)
break; break;
} else { } else {
pci_bus_read_config_dword(slot->bridge->pci_bus, u32 dvid;
pci_bus_read_config_dword(slot->bus,
PCI_DEVFN(slot->device, PCI_DEVFN(slot->device,
func->function), func->function),
PCI_VENDOR_ID, &dvid); PCI_VENDOR_ID, &dvid);
...@@ -827,23 +683,42 @@ static unsigned int get_slot_status(struct acpiphp_slot *slot) ...@@ -827,23 +683,42 @@ static unsigned int get_slot_status(struct acpiphp_slot *slot)
} }
/** /**
* acpiphp_eject_slot - physically eject the slot * trim_stale_devices - remove PCI devices that are not responding.
* @slot: ACPI PHP slot * @dev: PCI device to start walking the hierarchy from.
*/ */
int acpiphp_eject_slot(struct acpiphp_slot *slot) static void trim_stale_devices(struct pci_dev *dev)
{ {
struct acpiphp_func *func; acpi_handle handle = ACPI_HANDLE(&dev->dev);
struct pci_bus *bus = dev->subordinate;
bool alive = false;
list_for_each_entry(func, &slot->funcs, sibling) { if (handle) {
/* We don't want to call _EJ0 on non-existing functions. */ acpi_status status;
if ((func->flags & FUNC_HAS_EJ0)) { unsigned long long sta;
if (ACPI_FAILURE(acpi_evaluate_ej0(func->handle)))
return -1; status = acpi_evaluate_integer(handle, "_STA", NULL, &sta);
else alive = ACPI_SUCCESS(status) && sta == ACPI_STA_ALL;
break; }
} if (!alive) {
u32 v;
/* Check if the device responds. */
alive = pci_bus_read_dev_vendor_id(dev->bus, dev->devfn, &v, 0);
}
if (!alive) {
pci_stop_and_remove_bus_device(dev);
if (handle)
acpiphp_bus_trim(handle);
} else if (bus) {
struct pci_dev *child, *tmp;
/* The device is a bridge. so check the bus below it. */
pm_runtime_get_sync(&dev->dev);
list_for_each_entry_safe(child, tmp, &bus->devices, bus_list)
trim_stale_devices(child);
pm_runtime_put(&dev->dev);
} }
return 0;
} }
/** /**
...@@ -853,43 +728,30 @@ int acpiphp_eject_slot(struct acpiphp_slot *slot) ...@@ -853,43 +728,30 @@ int acpiphp_eject_slot(struct acpiphp_slot *slot)
* Iterate over all slots under this bridge and make sure that if a * Iterate over all slots under this bridge and make sure that if a
* card is present they are enabled, and if not they are disabled. * card is present they are enabled, and if not they are disabled.
*/ */
static int acpiphp_check_bridge(struct acpiphp_bridge *bridge) static void acpiphp_check_bridge(struct acpiphp_bridge *bridge)
{ {
struct acpiphp_slot *slot; struct acpiphp_slot *slot;
int retval = 0;
int enabled, disabled;
enabled = disabled = 0;
list_for_each_entry(slot, &bridge->slots, node) { list_for_each_entry(slot, &bridge->slots, node) {
unsigned int status = get_slot_status(slot); struct pci_bus *bus = slot->bus;
if (slot->flags & SLOT_ENABLED) { struct pci_dev *dev, *tmp;
if (status == ACPI_STA_ALL)
continue; mutex_lock(&slot->crit_sect);
retval = acpiphp_disable_slot(slot); /* wake up all functions */
if (retval) { if (get_slot_status(slot) == ACPI_STA_ALL) {
err("Error occurred in disabling\n"); /* remove stale devices if any */
goto err_exit; list_for_each_entry_safe(dev, tmp, &bus->devices,
} else { bus_list)
acpiphp_eject_slot(slot); if (PCI_SLOT(dev->devfn) == slot->device)
} trim_stale_devices(dev);
disabled++;
/* configure all functions */
enable_slot(slot);
} else { } else {
if (status != ACPI_STA_ALL) disable_slot(slot);
continue;
retval = acpiphp_enable_slot(slot);
if (retval) {
err("Error occurred in enabling\n");
goto err_exit;
}
enabled++;
} }
mutex_unlock(&slot->crit_sect);
} }
dbg("%s: %d enabled, %d disabled\n", __func__, enabled, disabled);
err_exit:
return retval;
} }
static void acpiphp_set_hpp_values(struct pci_bus *bus) static void acpiphp_set_hpp_values(struct pci_bus *bus)
...@@ -928,25 +790,6 @@ static void acpiphp_sanitize_bus(struct pci_bus *bus) ...@@ -928,25 +790,6 @@ static void acpiphp_sanitize_bus(struct pci_bus *bus)
* ACPI event handlers * ACPI event handlers
*/ */
static acpi_status
check_sub_bridges(acpi_handle handle, u32 lvl, void *context, void **rv)
{
struct acpiphp_bridge *bridge;
char objname[64];
struct acpi_buffer buffer = { .length = sizeof(objname),
.pointer = objname };
bridge = acpiphp_handle_to_bridge(handle);
if (bridge) {
acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer);
dbg("%s: re-enumerating slots under %s\n",
__func__, objname);
acpiphp_check_bridge(bridge);
put_bridge(bridge);
}
return AE_OK ;
}
void acpiphp_check_host_bridge(acpi_handle handle) void acpiphp_check_host_bridge(acpi_handle handle)
{ {
struct acpiphp_bridge *bridge; struct acpiphp_bridge *bridge;
...@@ -956,27 +799,23 @@ void acpiphp_check_host_bridge(acpi_handle handle) ...@@ -956,27 +799,23 @@ void acpiphp_check_host_bridge(acpi_handle handle)
acpiphp_check_bridge(bridge); acpiphp_check_bridge(bridge);
put_bridge(bridge); put_bridge(bridge);
} }
acpi_walk_namespace(ACPI_TYPE_DEVICE, handle,
ACPI_UINT32_MAX, check_sub_bridges, NULL, NULL, NULL);
} }
static void _handle_hotplug_event_bridge(struct work_struct *work) static void hotplug_event(acpi_handle handle, u32 type, void *data)
{ {
struct acpiphp_context *context = data;
struct acpiphp_func *func = &context->func;
struct acpiphp_bridge *bridge; struct acpiphp_bridge *bridge;
char objname[64]; char objname[64];
struct acpi_buffer buffer = { .length = sizeof(objname), struct acpi_buffer buffer = { .length = sizeof(objname),
.pointer = objname }; .pointer = objname };
struct acpi_hp_work *hp_work;
acpi_handle handle;
u32 type;
hp_work = container_of(work, struct acpi_hp_work, work); mutex_lock(&acpiphp_context_lock);
handle = hp_work->handle; bridge = context->bridge;
type = hp_work->type; if (bridge)
bridge = (struct acpiphp_bridge *)hp_work->context; get_bridge(bridge);
acpi_scan_lock_acquire(); mutex_unlock(&acpiphp_context_lock);
acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer);
...@@ -985,187 +824,129 @@ static void _handle_hotplug_event_bridge(struct work_struct *work) ...@@ -985,187 +824,129 @@ static void _handle_hotplug_event_bridge(struct work_struct *work)
/* bus re-enumerate */ /* bus re-enumerate */
dbg("%s: Bus check notify on %s\n", __func__, objname); dbg("%s: Bus check notify on %s\n", __func__, objname);
dbg("%s: re-enumerating slots under %s\n", __func__, objname); dbg("%s: re-enumerating slots under %s\n", __func__, objname);
acpiphp_check_bridge(bridge); if (bridge) {
acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, acpiphp_check_bridge(bridge);
ACPI_UINT32_MAX, check_sub_bridges, NULL, NULL, NULL); } else {
struct acpiphp_slot *slot = func->slot;
mutex_lock(&slot->crit_sect);
enable_slot(slot);
mutex_unlock(&slot->crit_sect);
}
break; break;
case ACPI_NOTIFY_DEVICE_CHECK: case ACPI_NOTIFY_DEVICE_CHECK:
/* device check */ /* device check */
dbg("%s: Device check notify on %s\n", __func__, objname); dbg("%s: Device check notify on %s\n", __func__, objname);
acpiphp_check_bridge(bridge); if (bridge)
break; acpiphp_check_bridge(bridge);
else
acpiphp_check_bridge(func->parent);
case ACPI_NOTIFY_DEVICE_WAKE:
/* wake event */
dbg("%s: Device wake notify on %s\n", __func__, objname);
break; break;
case ACPI_NOTIFY_EJECT_REQUEST: case ACPI_NOTIFY_EJECT_REQUEST:
/* request device eject */ /* request device eject */
dbg("%s: Device eject notify on %s\n", __func__, objname); dbg("%s: Device eject notify on %s\n", __func__, objname);
if ((bridge->flags & BRIDGE_HAS_EJ0) && bridge->func) { acpiphp_disable_and_eject_slot(func->slot);
struct acpiphp_slot *slot;
slot = bridge->func->slot;
if (!acpiphp_disable_slot(slot))
acpiphp_eject_slot(slot);
}
break; break;
}
case ACPI_NOTIFY_FREQUENCY_MISMATCH: if (bridge)
printk(KERN_ERR "Device %s cannot be configured due" put_bridge(bridge);
" to a frequency mismatch\n", objname); }
break;
case ACPI_NOTIFY_BUS_MODE_MISMATCH: static void hotplug_event_work(struct work_struct *work)
printk(KERN_ERR "Device %s cannot be configured due" {
" to a bus mode mismatch\n", objname); struct acpiphp_context *context;
break; struct acpi_hp_work *hp_work;
case ACPI_NOTIFY_POWER_FAULT: hp_work = container_of(work, struct acpi_hp_work, work);
printk(KERN_ERR "Device %s has suffered a power fault\n", context = hp_work->context;
objname); acpi_scan_lock_acquire();
break;
default: hotplug_event(hp_work->handle, hp_work->type, context);
warn("notify_handler: unknown event type 0x%x for %s\n", type, objname);
break;
}
acpi_scan_lock_release(); acpi_scan_lock_release();
kfree(hp_work); /* allocated in handle_hotplug_event_bridge */ kfree(hp_work); /* allocated in handle_hotplug_event() */
put_bridge(bridge); put_bridge(context->func.parent);
} }
/** /**
* handle_hotplug_event_bridge - handle ACPI event on bridges * handle_hotplug_event - handle ACPI hotplug event
* @handle: Notify()'ed acpi_handle * @handle: Notify()'ed acpi_handle
* @type: Notify code * @type: Notify code
* @context: pointer to acpiphp_bridge structure * @data: pointer to acpiphp_context structure
* *
* Handles ACPI event notification on {host,p2p} bridges. * Handles ACPI event notification on slots.
*/ */
static void handle_hotplug_event_bridge(acpi_handle handle, u32 type, static void handle_hotplug_event(acpi_handle handle, u32 type, void *data)
void *context)
{
struct acpiphp_bridge *bridge = context;
/*
* Currently the code adds all hotplug events to the kacpid_wq
* queue when it should add hotplug events to the kacpi_hotplug_wq.
* The proper way to fix this is to reorganize the code so that
* drivers (dock, etc.) do not call acpi_os_execute(), etc.
* For now just re-add this work to the kacpi_hotplug_wq so we
* don't deadlock on hotplug actions.
*/
get_bridge(bridge);
alloc_acpi_hp_work(handle, type, context, _handle_hotplug_event_bridge);
}
static void hotplug_event_func(acpi_handle handle, u32 type, void *context)
{ {
struct acpiphp_func *func = context; struct acpiphp_context *context;
char objname[64];
struct acpi_buffer buffer = { .length = sizeof(objname),
.pointer = objname };
acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer);
switch (type) { switch (type) {
case ACPI_NOTIFY_BUS_CHECK: case ACPI_NOTIFY_BUS_CHECK:
/* bus re-enumerate */
dbg("%s: Bus check notify on %s\n", __func__, objname);
acpiphp_enable_slot(func->slot);
break;
case ACPI_NOTIFY_DEVICE_CHECK: case ACPI_NOTIFY_DEVICE_CHECK:
/* device check : re-enumerate from parent bus */
dbg("%s: Device check notify on %s\n", __func__, objname);
acpiphp_check_bridge(func->slot->bridge);
break;
case ACPI_NOTIFY_DEVICE_WAKE:
/* wake event */
dbg("%s: Device wake notify on %s\n", __func__, objname);
break;
case ACPI_NOTIFY_EJECT_REQUEST: case ACPI_NOTIFY_EJECT_REQUEST:
/* request device eject */
dbg("%s: Device eject notify on %s\n", __func__, objname);
if (!(acpiphp_disable_slot(func->slot)))
acpiphp_eject_slot(func->slot);
break; break;
default: case ACPI_NOTIFY_DEVICE_WAKE:
warn("notify_handler: unknown event type 0x%x for %s\n", type, objname); return;
break;
}
}
static void _handle_hotplug_event_func(struct work_struct *work)
{
struct acpi_hp_work *hp_work;
struct acpiphp_func *func;
hp_work = container_of(work, struct acpi_hp_work, work); case ACPI_NOTIFY_FREQUENCY_MISMATCH:
func = hp_work->context; acpi_handle_err(handle, "Device cannot be configured due "
acpi_scan_lock_acquire(); "to a frequency mismatch\n");
return;
hotplug_event_func(hp_work->handle, hp_work->type, func); case ACPI_NOTIFY_BUS_MODE_MISMATCH:
acpi_handle_err(handle, "Device cannot be configured due "
"to a bus mode mismatch\n");
return;
acpi_scan_lock_release(); case ACPI_NOTIFY_POWER_FAULT:
kfree(hp_work); /* allocated in handle_hotplug_event_func */ acpi_handle_err(handle, "Device has suffered a power fault\n");
put_bridge(func->slot->bridge); return;
}
/** default:
* handle_hotplug_event_func - handle ACPI event on functions (i.e. slots) acpi_handle_warn(handle, "Unsupported event type 0x%x\n", type);
* @handle: Notify()'ed acpi_handle return;
* @type: Notify code }
* @context: pointer to acpiphp_func structure
*
* Handles ACPI event notification on slots.
*/
static void handle_hotplug_event_func(acpi_handle handle, u32 type,
void *context)
{
struct acpiphp_func *func = context;
/* mutex_lock(&acpiphp_context_lock);
* Currently the code adds all hotplug events to the kacpid_wq context = acpiphp_get_context(handle);
* queue when it should add hotplug events to the kacpi_hotplug_wq. if (context) {
* The proper way to fix this is to reorganize the code so that get_bridge(context->func.parent);
* drivers (dock, etc.) do not call acpi_os_execute(), etc. acpiphp_put_context(context);
* For now just re-add this work to the kacpi_hotplug_wq so we alloc_acpi_hp_work(handle, type, context, hotplug_event_work);
* don't deadlock on hotplug actions. }
*/ mutex_unlock(&acpiphp_context_lock);
get_bridge(func->slot->bridge);
alloc_acpi_hp_work(handle, type, context, _handle_hotplug_event_func);
} }
/* /*
* Create hotplug slots for the PCI bus. * Create hotplug slots for the PCI bus.
* It should always return 0 to avoid skipping following notifiers. * It should always return 0 to avoid skipping following notifiers.
*/ */
void acpiphp_enumerate_slots(struct pci_bus *bus, acpi_handle handle) void acpiphp_enumerate_slots(struct pci_bus *bus)
{ {
struct acpiphp_bridge *bridge; struct acpiphp_bridge *bridge;
acpi_handle handle;
acpi_status status;
if (acpiphp_disabled) if (acpiphp_disabled)
return; return;
if (detect_ejectable_slots(handle) <= 0) handle = ACPI_HANDLE(bus->bridge);
if (!handle)
return; return;
bridge = kzalloc(sizeof(struct acpiphp_bridge), GFP_KERNEL); bridge = kzalloc(sizeof(struct acpiphp_bridge), GFP_KERNEL);
if (bridge == NULL) { if (!bridge) {
err("out of memory\n"); acpi_handle_err(handle, "No memory for bridge object\n");
return; return;
} }
INIT_LIST_HEAD(&bridge->slots); INIT_LIST_HEAD(&bridge->slots);
kref_init(&bridge->ref); kref_init(&bridge->ref);
bridge->handle = handle;
bridge->pci_dev = pci_dev_get(bus->self); bridge->pci_dev = pci_dev_get(bus->self);
bridge->pci_bus = bus; bridge->pci_bus = bus;
...@@ -1176,30 +957,62 @@ void acpiphp_enumerate_slots(struct pci_bus *bus, acpi_handle handle) ...@@ -1176,30 +957,62 @@ void acpiphp_enumerate_slots(struct pci_bus *bus, acpi_handle handle)
*/ */
get_device(&bus->dev); get_device(&bus->dev);
if (!pci_is_root_bus(bridge->pci_bus) && if (!pci_is_root_bus(bridge->pci_bus)) {
acpi_has_method(bridge->handle, "_EJ0")) { struct acpiphp_context *context;
dbg("found ejectable p2p bridge\n");
bridge->flags |= BRIDGE_HAS_EJ0; /*
bridge->func = acpiphp_bridge_handle_to_function(handle); * This bridge should have been registered as a hotplug function
* under its parent, so the context has to be there. If not, we
* are in deep goo.
*/
mutex_lock(&acpiphp_context_lock);
context = acpiphp_get_context(handle);
if (WARN_ON(!context)) {
mutex_unlock(&acpiphp_context_lock);
put_device(&bus->dev);
kfree(bridge);
return;
}
bridge->context = context;
context->bridge = bridge;
/* Get a reference to the parent bridge. */
get_bridge(context->func.parent);
mutex_unlock(&acpiphp_context_lock);
} }
init_bridge_misc(bridge); /* must be added to the list prior to calling register_slot */
mutex_lock(&bridge_mutex);
list_add(&bridge->list, &bridge_list);
mutex_unlock(&bridge_mutex);
/* register all slot objects under this bridge */
status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1,
register_slot, NULL, bridge, NULL);
if (ACPI_FAILURE(status)) {
acpi_handle_err(handle, "failed to register slots\n");
cleanup_bridge(bridge);
put_bridge(bridge);
}
} }
/* Destroy hotplug slots associated with the PCI bus */ /* Destroy hotplug slots associated with the PCI bus */
void acpiphp_remove_slots(struct pci_bus *bus) void acpiphp_remove_slots(struct pci_bus *bus)
{ {
struct acpiphp_bridge *bridge, *tmp; struct acpiphp_bridge *bridge;
if (acpiphp_disabled) if (acpiphp_disabled)
return; return;
list_for_each_entry_safe(bridge, tmp, &bridge_list, list) mutex_lock(&bridge_mutex);
list_for_each_entry(bridge, &bridge_list, list)
if (bridge->pci_bus == bus) { if (bridge->pci_bus == bus) {
mutex_unlock(&bridge_mutex);
cleanup_bridge(bridge); cleanup_bridge(bridge);
put_bridge(bridge); put_bridge(bridge);
break; return;
} }
mutex_unlock(&bridge_mutex);
} }
/** /**
...@@ -1208,51 +1021,39 @@ void acpiphp_remove_slots(struct pci_bus *bus) ...@@ -1208,51 +1021,39 @@ void acpiphp_remove_slots(struct pci_bus *bus)
*/ */
int acpiphp_enable_slot(struct acpiphp_slot *slot) int acpiphp_enable_slot(struct acpiphp_slot *slot)
{ {
int retval;
mutex_lock(&slot->crit_sect); mutex_lock(&slot->crit_sect);
/* configure all functions */
if (!(slot->flags & SLOT_ENABLED))
enable_slot(slot);
/* wake up all functions */
retval = power_on_slot(slot);
if (retval)
goto err_exit;
if (get_slot_status(slot) == ACPI_STA_ALL) {
/* configure all functions */
retval = enable_device(slot);
if (retval)
power_off_slot(slot);
} else {
dbg("%s: Slot status is not ACPI_STA_ALL\n", __func__);
power_off_slot(slot);
}
err_exit:
mutex_unlock(&slot->crit_sect); mutex_unlock(&slot->crit_sect);
return retval; return 0;
} }
/** /**
* acpiphp_disable_slot - power off slot * acpiphp_disable_and_eject_slot - power off and eject slot
* @slot: ACPI PHP slot * @slot: ACPI PHP slot
*/ */
int acpiphp_disable_slot(struct acpiphp_slot *slot) int acpiphp_disable_and_eject_slot(struct acpiphp_slot *slot)
{ {
struct acpiphp_func *func;
int retval = 0; int retval = 0;
mutex_lock(&slot->crit_sect); mutex_lock(&slot->crit_sect);
/* unconfigure all functions */ /* unconfigure all functions */
retval = disable_device(slot); disable_slot(slot);
if (retval)
goto err_exit; list_for_each_entry(func, &slot->funcs, sibling)
if (func->flags & FUNC_HAS_EJ0) {
acpi_handle handle = func_to_handle(func);
/* power off all functions */ if (ACPI_FAILURE(acpi_evaluate_ej0(handle)))
retval = power_off_slot(slot); acpi_handle_err(handle, "_EJ0 failed\n");
if (retval)
goto err_exit; break;
}
err_exit:
mutex_unlock(&slot->crit_sect); mutex_unlock(&slot->crit_sect);
return retval; return retval;
} }
...@@ -1264,7 +1065,7 @@ int acpiphp_disable_slot(struct acpiphp_slot *slot) ...@@ -1264,7 +1065,7 @@ int acpiphp_disable_slot(struct acpiphp_slot *slot)
*/ */
u8 acpiphp_get_power_status(struct acpiphp_slot *slot) u8 acpiphp_get_power_status(struct acpiphp_slot *slot)
{ {
return (slot->flags & SLOT_POWEREDON); return (slot->flags & SLOT_ENABLED);
} }
...@@ -1274,11 +1075,7 @@ u8 acpiphp_get_power_status(struct acpiphp_slot *slot) ...@@ -1274,11 +1075,7 @@ u8 acpiphp_get_power_status(struct acpiphp_slot *slot)
*/ */
u8 acpiphp_get_latch_status(struct acpiphp_slot *slot) u8 acpiphp_get_latch_status(struct acpiphp_slot *slot)
{ {
unsigned int sta; return !(get_slot_status(slot) & ACPI_STA_DEVICE_UI);
sta = get_slot_status(slot);
return (sta & ACPI_STA_DEVICE_UI) ? 0 : 1;
} }
...@@ -1288,9 +1085,5 @@ u8 acpiphp_get_latch_status(struct acpiphp_slot *slot) ...@@ -1288,9 +1085,5 @@ u8 acpiphp_get_latch_status(struct acpiphp_slot *slot)
*/ */
u8 acpiphp_get_adapter_status(struct acpiphp_slot *slot) u8 acpiphp_get_adapter_status(struct acpiphp_slot *slot)
{ {
unsigned int sta; return !!get_slot_status(slot);
sta = get_slot_status(slot);
return (sta == 0) ? 0 : 1;
} }
...@@ -66,7 +66,7 @@ do { \ ...@@ -66,7 +66,7 @@ do { \
#define IBM_HARDWARE_ID1 "IBM37D0" #define IBM_HARDWARE_ID1 "IBM37D0"
#define IBM_HARDWARE_ID2 "IBM37D4" #define IBM_HARDWARE_ID2 "IBM37D4"
#define hpslot_to_sun(A) (((struct slot *)((A)->private))->acpi_slot->sun) #define hpslot_to_sun(A) (((struct slot *)((A)->private))->sun)
/* union apci_descriptor - allows access to the /* union apci_descriptor - allows access to the
* various device descriptors that are embedded in the * various device descriptors that are embedded in the
......
...@@ -210,7 +210,7 @@ static int acpi_pci_set_power_state(struct pci_dev *dev, pci_power_t state) ...@@ -210,7 +210,7 @@ static int acpi_pci_set_power_state(struct pci_dev *dev, pci_power_t state)
} }
if (!error) if (!error)
dev_info(&dev->dev, "power state changed by ACPI to %s\n", dev_dbg(&dev->dev, "power state changed by ACPI to %s\n",
acpi_power_state_string(state_conv[state])); acpi_power_state_string(state_conv[state]));
return error; return error;
...@@ -290,24 +290,16 @@ static struct pci_platform_pm_ops acpi_pci_platform_pm = { ...@@ -290,24 +290,16 @@ static struct pci_platform_pm_ops acpi_pci_platform_pm = {
void acpi_pci_add_bus(struct pci_bus *bus) void acpi_pci_add_bus(struct pci_bus *bus)
{ {
acpi_handle handle = NULL; if (acpi_pci_disabled || !bus->bridge)
if (bus->bridge)
handle = ACPI_HANDLE(bus->bridge);
if (acpi_pci_disabled || handle == NULL)
return; return;
acpi_pci_slot_enumerate(bus, handle); acpi_pci_slot_enumerate(bus);
acpiphp_enumerate_slots(bus, handle); acpiphp_enumerate_slots(bus);
} }
void acpi_pci_remove_bus(struct pci_bus *bus) void acpi_pci_remove_bus(struct pci_bus *bus)
{ {
/* if (acpi_pci_disabled || !bus->bridge)
* bus->bridge->acpi_node.handle has already been reset to NULL
* when acpi_pci_remove_bus() is called, so don't check ACPI handle.
*/
if (acpi_pci_disabled)
return; return;
acpiphp_remove_slots(bus); acpiphp_remove_slots(bus);
......
...@@ -47,24 +47,22 @@ void acpi_pci_remove_bus(struct pci_bus *bus); ...@@ -47,24 +47,22 @@ void acpi_pci_remove_bus(struct pci_bus *bus);
#ifdef CONFIG_ACPI_PCI_SLOT #ifdef CONFIG_ACPI_PCI_SLOT
void acpi_pci_slot_init(void); void acpi_pci_slot_init(void);
void acpi_pci_slot_enumerate(struct pci_bus *bus, acpi_handle handle); void acpi_pci_slot_enumerate(struct pci_bus *bus);
void acpi_pci_slot_remove(struct pci_bus *bus); void acpi_pci_slot_remove(struct pci_bus *bus);
#else #else
static inline void acpi_pci_slot_init(void) { } static inline void acpi_pci_slot_init(void) { }
static inline void acpi_pci_slot_enumerate(struct pci_bus *bus, static inline void acpi_pci_slot_enumerate(struct pci_bus *bus) { }
acpi_handle handle) { }
static inline void acpi_pci_slot_remove(struct pci_bus *bus) { } static inline void acpi_pci_slot_remove(struct pci_bus *bus) { }
#endif #endif
#ifdef CONFIG_HOTPLUG_PCI_ACPI #ifdef CONFIG_HOTPLUG_PCI_ACPI
void acpiphp_init(void); void acpiphp_init(void);
void acpiphp_enumerate_slots(struct pci_bus *bus, acpi_handle handle); void acpiphp_enumerate_slots(struct pci_bus *bus);
void acpiphp_remove_slots(struct pci_bus *bus); void acpiphp_remove_slots(struct pci_bus *bus);
void acpiphp_check_host_bridge(acpi_handle handle); void acpiphp_check_host_bridge(acpi_handle handle);
#else #else
static inline void acpiphp_init(void) { } static inline void acpiphp_init(void) { }
static inline void acpiphp_enumerate_slots(struct pci_bus *bus, static inline void acpiphp_enumerate_slots(struct pci_bus *bus) { }
acpi_handle handle) { }
static inline void acpiphp_remove_slots(struct pci_bus *bus) { } static inline void acpiphp_remove_slots(struct pci_bus *bus) { }
static inline void acpiphp_check_host_bridge(acpi_handle handle) { } static inline void acpiphp_check_host_bridge(acpi_handle handle) { }
#endif #endif
......
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